Kids Hobby Prediction Dataset

Problem

Children often find themselves at a crossroads when it comes to discovering their passions or hobbies, be it in academics, arts, or sports. Recognizing the importance of guiding children towards activities they are passionate about; we have curated a dataset obtained through surveys conducted with parents. This dataset compiles valuable information about children’s preferences, enabling the creation of a classification model aimed at predicting kids’ hobbies.

In this exploratory journey, we delve into the dataset to uncover patterns and insights that can assist parents in understanding their child’s inclinations better. Through the application of clustering techniques, we aim to categorize children based on number of columns (Fav_sub , Scholarship, etc..). The ultimate goal is to provide parents with meaningful recommendations, fostering an environment where children can thrive in activities that genuinely resonate with their interests.

By analyzing this dataset, we hope to offer insightful information that will aid parents in guiding their kids toward the right hobbies.

Data Mining Task

The data mining task at hand revolves around predicting kids’ hobbies based on a dataset named “Hobby_Data,” obtained through specific questions posed to their parents regarding preferences, capabilities, and achievements. This dataset serves as the foundation for two primary data mining tasks: classification and clustering. In the classification process, the goal is to train a machine learning model to be capable of accurately predicting a child’s hobby as either “academic,” “art,” or “sports.” This requires using the data collected from parents to establish patterns and relationships that guide the model in making accurate predictions. Concurrently, the clustering process involves partitioning the dataset into meaningful clusters, grouping together children with similar characteristics or preferences. Through these dual approaches, the data mining task aims to develop a robust predictive model capable of accurately categorizing children’s hobbies in order to uncover the inherent structures, patterns, and associations within the dataset, contributing to a deeper understanding of the diverse interests and engagement levels of the young population in academic, artistic, and sports-related activities.

Data

The source: https://www.kaggle.com/datasets/abtabm/hobby-prediction-basic Number of objects: 1601

Number of attributes: 14

Description: Following the selection of our data set (“Hobby_Data”) which predicts kids’ hobbies, that was collected by asking their parents specific questions about their kid’s preferences, capabilities, and achievements. To help us train the machine to predict the kid’s hobby. We will begin to preprocess and analyze the data.

Attribute Description:

Attribute name Description Data type
Olympiad_Participation Has your child participated in any Science/Maths Boolean
Scholarship Has he/she received any scholarship? Boolean
School Love’s going to school? Boolean
Fav_sub What is his/her favorite subject? Categorical
Projects Has done any projects under academics before? Boolean
Grasp_pow His/Her Grasping power (1-6) Ordinal
Time_sprt How much time does he/she spend playing outdoor/indoor games? Ordinal
Medals Medals won in Sports? Boolean
Career_sprt Want’s to pursue his/her career in sports? Boolean
Act_sprt Regular in his/her sports activities? Boolean
Fant_arts Love creating fantasy paintings? Boolean
Won_arts Won art competitions? Ordinal
Time_art Time utilized in Arts? Ordinal
Predicted Hobby predictions for the hobby that the kid wouldl ike Categorical

======= >>>>>>> Stashed changes

General information about the data set:

str(Hobby_Data)
sample(Hobby_Data)

variables distribution of Time_sprt:

install.packages("magrittr") # install only one time then put this command as comment after installation
library(magrittr) ## for pipe operations
Hobby_Data$Time_art %>% density() %>% plot(main='variables distribution of Time_art')

In the “Time_art” variable, parents were requested to assess the time their child dedicated to artistic pursuits like painting or paper crafting, using a scale ranging from 1 to 6, where 6 represents the highest level of involvement. It’s worth noting that the concentration of lower ratings at the lower end of the scale (1) is quite pronounced, and this tendency may be attributed to the inherent inclination of children towards physical activities.

variables distribution of Time_art:

Hobby_Data$Time_sprt %>% density() %>% plot(main='variables distribution of Time_sprt')

Parents were requested to assess their children’s involvement in sports on a scale from 1 to 6 within the “Time_sprt” variable. Notably, the most prevalent ranking was 3, suggesting a moderate level of sports participation. It’s interesting to observe that the distribution exhibits a shape akin to a bell curve, indicating that a substantial proportion of children have a genuine love for sports.

variables distribution of Grasp_pow:

Hobby_Data$Grasp_pow %>% density() %>% plot(main='variables distribution of Grasp_pow')

The density graph depicting parents’ ratings of their children’s grasp power, which ranges from 1 to 6, illustrates a trend where the most common rating is level 3, followed by level 4, level 5, level 2, level 1, and level 6. This distribution indicates that a substantial proportion of parents believe their children possess grasp power that is average or slightly above average (levels 3 and 4), with fewer children rated at the extremes (levels 1, 2, 5, and 6).

variables distribution of the class label ‘Predicted Hobby’:

install.packages("dplyr") # install only one time then put this command as comment after installation
library(dplyr)

dataset2 <- Hobby_Data %>% sample_n(1600)
table(dataset2$`Predicted Hobby`) %>% pie()
tab <- dataset2$`Predicted Hobby` %>% table()
precentages <- tab %>% prop.table() %>% round(3) * 100 
txt <- paste0(names(tab), '\n', precentages, '%')
pie(tab, labels=txt)

The pie chart illustrates the distribution of the class label ‘Predicted Hobby’. It’s evident that a substantial portion, approximately 43.7%, of the children’s hobbies are academic in nature, indicating a strong interest in educational pursuits. Additionally, 30.8% of the kids are engaged in sports, reflecting a significant inclination towards physical activities. Arts-related hobbies account for 25.6% of the total, the distribution reflects a harmonious blend of hobbies. This balanced distribution not only signifies a variety of interests but also indicates a well-rounded engagement of children in academic, physical, and creative activities.

Statistical measures :

find_mode <- function(x) {
  u <- unique(x)
  tab <- tabulate(match(x, u))
  u[tab == max(tab)]
}

find_mode(Hobby_Data$Time_art)
# Updated upstream
hist(Hobby_Data$Time_art)

#Stashed changes

The histogram makes it evident that the mode is equal to 1, and we believe that the high frequency of parents ranking “1” as the most chosen rank in the “Time_art” variable could be attributed to several factors. It might indicate that a significant number of parents perceive their children’s involvement in art activities as relatively low, possibly due to time constraints, academic priorities, or a limited interest in art. Alternatively, it could reflect that parents value a more balanced approach to their children’s activities, with a variety of interests and responsibilities sharing their time. This trend could also result from a cultural or educational emphasis on other subjects and extracurricular activities that compete for a child’s time, potentially leading to a lower ranking for art-related activities.

find_mode(Hobby_Data$Grasp_pow)
hist(Hobby_Data$Grasp_pow)

Rank 3, in this context, may have been the most chosen rank because it likely represents an average or moderate level of grasp power. Parents may have assessed their children’s grasp power as neither exceptionally strong (rank 5 or 6) nor particularly weak (rank 1 or 2), resulting in the preference for the middle-ranking option. This choice could reflect a perception that their children’s grasp power falls within a typical or expected range, making it the most common rating.

find_mode(Hobby_Data$Time_sprt)
hist(Hobby_Data$Time_sprt)

As shown in the histogram the rank 3 on a scale of 1 to 6 was likely the most chosen rank for assessing children’s involvement in sports because it represents a balanced middle ground. Parents may have perceived rank 3 as indicating that their children are moderately involved in sports, not excessively committed or disinterested. This middle-of-the-road ranking reflects a common perspective that many parents may hold, considering that extreme rankings, such as 1 or 6, might suggest either a lack of involvement or an excessive focus on sports, which may not align with their perception of their child’s overall well-rounded development.

The histogram graph for “Time_art” variable

hist(Hobby_Data$Time_art)

The histogram makes it evident that the mode is equal to 1, and we believe that the high frequency of parents ranking “1” as the most chosen rank in the “Time_art” variable could be attributed to several factors. It might indicate that a significant number of parents perceive their children’s involvement in art activities as relatively low, possibly due to time constraints, academic priorities, or a limited interest in art. Alternatively, it could reflect that parents value a more balanced approach to their children’s activities, with a variety of interests and responsibilities sharing their time. This trend could also result from a cultural or educational emphasis on other subjects and extracurricular activities that compete for a child’s time, potentially leading to a lower ranking for art-related activities.

find_mode(Hobby_Data$Grasp_pow)

The histogram graph for “Grasp_pow” variable

hist(Hobby_Data$Grasp_pow)

Rank 3, in this context, may have been the most chosen rank because it likely represents an average or moderate level of grasp power. Parents may have assessed their children’s grasp power as neither exceptionally strong (rank 5 or 6) nor particularly weak (rank 1 or 2), resulting in the preference for the middle-ranking option. This choice could reflect a perception that their children’s grasp power falls within a typical or expected range, making it the most common rating.

find_mode(Hobby_Data$Time_sprt)

The histogram graph for “Time_sprt” variable

hist(Hobby_Data$Time_sprt)

As shown in the histogram the rank 3 on a scale of 1 to 6 was likely the most chosen rank for assessing children’s involvement in sports because it represents a balanced middle ground. Parents may have perceived rank 3 as indicating that their children are moderately involved in sports, not excessively committed or disinterested. This middle-of-the-road ranking reflects a common perspective that many parents may hold, considering that extreme rankings, such as 1 or 6, might suggest either a lack of involvement or an excessive focus on sports, which may not align with their perception of their child’s overall well-rounded development.

Data preprocessing

#1# Data cleaning:

During the data cleaning stage, finding and fixing faults, inconsistencies, and errors in a dataset helps it be more reliable and of higher quality for analysis and modeling. There are methods for handling missing values, detecting outliers, resolving inconsistencies, and standardizing formats.

import Dataset”Hobby_Data”


View(Hobby_Data)

str(Hobby_Data)

check missing value :

is.na(Hobby_Data)

find the total null values in the dataset:

sum(is.na(Hobby_Data))

Description:

This stage involves checking and deleting for null and missing values because they might have a significant impact on the data and cause errors and negative effects in subsequent steps.We simply looked for missing values, and there are a missing values in our dataset. According to our investigation, the dataset does not contain any outliers since it doesn’t have a numerical data type. Additionally, there are no inconsistent values or other errors.

Dataset after cleaning step:

sample(Hobby_Data)

#2#Encoding: This step includes a Converting categorical or non-numeric data into a numerical format, which is necessary for compatibility with subsequent steps in preprocessing.

Hobby_Data$Olympiad_Participation = factor(Hobby_Data$Olympiad_Participation,levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Scholarship = factor(Hobby_Data$Scholarship , levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$School = factor(Hobby_Data$School, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Projects = factor(Hobby_Data$Projects, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Medals = factor(Hobby_Data$Medals, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Career_sprt = factor(Hobby_Data$Career_sprt, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Act_sprt = factor(Hobby_Data$Act_sprt, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Fant_arts = factor(Hobby_Data$Fant_arts, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Won_arts = factor(Hobby_Data$Won_arts, levels = c("No", "Maybe", "Yes"), labels = c(0, 2, 1))
Hobby_Data$Fav_sub = factor(Hobby_Data$Fav_sub, levels = c("Science", "Mathematics", "History/Geography", "Any language"), labels = c(1, 2, 3, 4))
Hobby_Data$`Predicted Hobby` <- factor(Hobby_Data$`Predicted Hobby`, levels = c("Academics", "Arts", "Sports"), labels = c(1, 2, 3))

Dataset after Encoding :

sample(Hobby_Data)

#3# Normalization and Discetization:

We don’t need to use normalization and discetization in our dataset. Since our dataset doesn’t have numeric attributes and normalization involves mathematical operations, which can result in meaningless values and errors, Also, applying discretization leads to a loss of information and creates intervals and relationships that don’t exist between values.

#4#Feature Selection:

To improve the accuracy of our predictions for the target class “Predicted Hobby” and decrease the processing time of our classifier, we will utilize feature selection techniques. These techniques enable us to eliminate redundant or irrelevant attributes from the dataset, resulting in a more concise subset of features that provide the most valuable information for our predictions.

Specifically, we will use two feature selection methods: Rank Features by Importance and Feature Selection Using Recursive Feature Elimination (RFE).

1.Rank Features by Importance:

This method used in the code helps us determine which features are most important for predicting the “Predicted Hobby” class label in the dataset. It utilizes the Random Forest algorithm, known for its accurate prediction capabilities. This method calculates the importance of each feature by assessing its contribution to the overall accuracy of the predictions. By ranking the features based on their importance, we can identify the ones that have the greatest influence on determining hobbies.

Ensure the results are repeatable by setting a seed:

set.seed(7)

Load the necessary libraries:

install.packages("caret")
install.packages("randomForest")
library(caret)
library(randomForest)

Separate the predictors and the class label:

predictors <- Hobby_Data[, -14]  # Excluding the class label (Predicted Hobby)
class_label <- Hobby_Data$`Predicted Hobby`

Train a Random Forest model:

model <- randomForest(predictors, class_label, importance = TRUE)

Get the variable importance:

importance <- importance(model)

Rank the features by importance:

ranked_features <- sort(importance[, "MeanDecreaseGini"], decreasing = TRUE)

Print the ranked features:

print(ranked_features)

barplot(ranked_features, horiz = TRUE, las = 1, main = "Kids Hobby Variable Importance Ranking")

2.Feature Selection Using RFE:

The Recursive Feature Elimination (RFE) method with Random Forest is a technique used to select the most important features for accurate predictions . It iteratively eliminates less relevant features, retraining the model at each step to evaluate performance. By focusing on the most informative features, RFE improves reduces complexity, and enhances prediction accuracy. It is particularly effective with Random Forest due to its ability to handle complex relationships and high-dimensional data. RFE helps identify the most important attributes associated with the target variable, enabling the creation of more efficient and accurate models.

Ensure the results are repeatable by setting a seed:

set.seed(7)

Load the necessary libraries:

library(caret)

Define the control parameters for RFE using random forest selection function:

control <- rfeControl(functions = rfFuncs, method = "cv", number = 10)

Extract the predictor variables from Hobby_Data:

predictors <- Hobby_Data[, -ncol(Hobby_Data)]

Convert the outcome variable to a factor:

outcome <- as.factor(Hobby_Data$`Predicted Hobby`)

Run the RFE algorithm:

results <- rfe(predictors, outcome, sizes = 1:ncol(Hobby_Data), rfeControl = control)

Summarize the results:

print(results)

List the chosen features selected by RFE:

predictors(results)

Plot the results:

plot(results, type = c("g", "o"))

3.Removing Irrelevant Columns:

By considering both Recursive Feature Elimination (RFE) and Rank By Importance, we can make informed decisions about feature relevance and impact on the model. In this case, the columns “School,” “Medals” should be deleted as they have lower importance scores compared to the selected variables. Removing these columns simplifies the model and reduces dimensionality, eliminating potential noise and irrelevant information that could hinder accurate predictions.

Remove the specified columns from the Hobby_Kids dataset:

Hobby_Data <- Hobby_Data[, !(colnames(Hobby_Data) %in% c("School", "Medals"))]

Display the updated dataset after deleting columns:

sample(Hobby_Data)

we can see that the two columns are deleted(“School”, “Medals”).

Balanced Data

# Calculate class imbalance
class_imbalance <- max(prop.table(table(Hobby_Data$`Predicted Hobby`))) - min(prop.table(table(Hobby_Data$`Predicted Hobby`)))

# Print the result
print(class_imbalance)

if class_imbalance is close to 0, it suggests that the proportions of different classes are relatively similar, indicating a balanced dataset. Conversely, if class_imbalance is larger, it suggests a more significant imbalance between the classes. The calculated class imbalance value of 0.1805122 suggests that the distribution of classes in the “Predicted_Hobby” column of the dataset is relatively balanced. The class imbalance is a measure of the difference between the proportions of the most prevalent and least prevalent classes. In this case, the value is close to 0, indicating that there is a minimal difference between the proportions of different classes. A lower-class imbalance value is generally desirable, as it signifies a more even distribution of instances across classes, which can be beneficial for the training and performance of machine learning models.

Data Mining Technique

Evaluation And Comparison

————————————————— Classification—————————————————-

After preprocessing, we will proceed to the classification step. In this phase, as part of supervised learning, we will apply a classification algorithm to assign each data point into predefined categories based on its attributes. This involves selecting the most relevant features that have been cleaned and formatted during preprocessing. The selected model will then learn from training data, enabling it to predict the category of new, unseen data accurately. This step is essential for making informed decisions or predictions based on the data.

We implement a decision tree on the dataset, which has been partitioned into Training and Test sets using the percentage split method. This method ensures that each subset is a randomized, representative sample of the entire dataset, thus minimizing bias and enabling consistent model performance evaluation. We choose three different split sizes: (“90%”, “10%”), (“80%”, “20%”), and (“70%”, “30%”). These varying sizes are selected to provide insight into the model’s performance with different amounts of data, which is vital for detecting unique patterns and confirming the model’s consistency in various situations.

In the final steps, we utilize data visualization tools to create visual representations of our decision trees. Additionally, we conduct an exhaustive evaluation of the model, employing a “Confusion Matrix” to illustrate the outcomes clearly.

Information Gain

Information Gain is particularly useful when dealing with categorical target variables using Information Gain for the initial partitioning of a dataset when building a decision tree It’s based on the concept of entropy and aims to maximize the homogeneity of subsets after each split. This approach helps create an effective decision tree by selecting features that provide the most information for predicting the target variable.

1-Information Gain(70%,30%)

# Load the required packages
library(rpart)
library(rpart.plot)
library(caret)

# Set the seed for reproducibility
set.seed(1234)

# Split the data into training and testing sets
ind <- sample(2, nrow(Hobby_Data), replace=TRUE, prob=c(0.7, 0.3))
trainData <- Hobby_Data[ind == 1,]
testData <- Hobby_Data[ind == 2,]

# Define the formula for the decision tree
myFormula <- `Predicted Hobby` ~ Scholarship + Fav_sub + Projects + Grasp_pow + Time_sprt + Career_sprt + Act_sprt + Fant_arts + Won_arts + Time_art + Olympiad_Participation

# Create the decision tree model with the "information" splitting criterion
Hobby_Data_ctree <- rpart(myFormula, data = trainData, method = "class", parms = list(split = "information"))





# Print the decision tree
print(Hobby_Data_ctree)

# Plot the decision tree

rpart.plot(Hobby_Data_ctree)

Decision Tree Analysis Using Information gain(70/30):

In Frist Tree ,we devide dataset into training set and test set with size(%70,%30) respectively. As you can see in the figure, the root node (“Career_sprt”) serves as the starting point for the classification process since have the heights Gain. The dataset has a distribution of approximately 42.47% for class 1(“Academics”), 25.92% for class 2(“Arts”), and 31.61% for class 3 (“Sports”).

The tree further branches based on the values of the “Career_sprt” if equal 0, tree branches based on”Won_arts” ,and majority of instances fall into “Acadimcs”, constituting 63.93% , if”Won_arts” equal 0 or2 In this case the tree terminates with a leaf node indicating a high probability 89.08% for “Acadimcs”.else the instances are classified based on “Fant_arts” into “Arts” with a probability 88%,if equal 0 the instances are classified into “Academics” with a probability of 73% ,else the instances are classified into “Arts” with a high probability 96%, if “Career_sprt” is 1, the tree further branches based on the “Fant_arts” into “Sports” with a probability 80%. If “Fant_arts” is 1 , there is another split based on the “Time_art” feature into “Arts” with a probability 49%. If “Time_art” is greater than or equal to 3, the instances are classified into “Arts” with a probability of 87% . On the other hand, if “Time_art” is less than 3, the instances are classified into “Sports” with a probability of 65% .If “Fant_arts” not equal 1 , the instances are classified into “Sports” with a high probability 95%, as indicated by the leaf node.

First Confusion matrix

# Predict on the test data
testPred <- predict(Hobby_Data_ctree, newdata = testData, type = 'class')

# Check the accuracy of the model
accuracy <- sum(testPred == testData$`Predicted Hobby`) / nrow(testData) * 100
cat('Accuracy:', accuracy, '\n')

# Create a confusion matrix
conf_matrix <- table(Actual = testData$`Predicted Hobby`, Predicted = testPred)

# Calculate precision for each class
precision_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
precision_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
precision_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])

# Calculate sensitivity for each class
sensitivity_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
sensitivity_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
sensitivity_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])

# Calculate specificity for each class
specificity_class_1 <- sum(diag(conf_matrix[-1, -1])) / sum(conf_matrix[-1, ])
specificity_class_2 <- sum(conf_matrix[c(1, 3), c(1, 3)]) / sum(conf_matrix[c(1, 3), ])
specificity_class_3 <- sum(conf_matrix[c(1, 2), c(1, 2)]) / sum(conf_matrix[c(1, 2), ])

# Calculate macro-average sensitivity
macro_avg_sensitivity <- (sensitivity_class_1 + sensitivity_class_2 + sensitivity_class_3) / 3

# Calculate macro-average specificity
macro_avg_specificity <- (specificity_class_1 + specificity_class_2 + specificity_class_3) / 3

# Calculate macro-average precision
macro_avg_precision <- (precision_class_1 + precision_class_2 + precision_class_3) / 3

# Print macro-average sensitivity
cat('Average Sensitivity:', macro_avg_sensitivity, '\n')

# Print macro-average specificity
cat('Average Specificity:', macro_avg_specificity, '\n')

# Print macro-average precision
cat('Average Precision:', macro_avg_precision, '\n')






# Print precision for each class
cat('Precision for Class 1:', precision_class_1, ' \n')

cat('Precision for Class 2:', precision_class_2, '  \n')
cat('Precision for Class 3:', precision_class_3, ' \n')

# Print sensitivity for each class
cat('Sensitivity for Class 1:', sensitivity_class_1, '\n')
cat('Sensitivity for Class 2:', sensitivity_class_2, '\n')
cat('Sensitivity for Class 3:', sensitivity_class_3, '\n')

# Print specificity for each class
cat('Specificity for Class 1:', specificity_class_1, '\n')
cat('Specificity for Class 2:', specificity_class_2, '\n')
cat('Specificity for Class 3:', specificity_class_3, '\n')

2-Information Gain(80%,20%)

library(rpart)

library(caTools)
library(rpart.plot)
library(caret)

# Set the seed for reproducibility
set.seed(1234)

# Split the data into training and testing sets
ind <- sample(2, nrow(Hobby_Data), replace=TRUE, prob=c(0.8, 0.2))
trainData <- Hobby_Data[ind == 1,]
testData <- Hobby_Data[ind == 2,]

# Define the formula for the decision tree
myFormula <- `Predicted Hobby` ~ Scholarship + Fav_sub + Projects + Grasp_pow + Time_sprt + Career_sprt + Act_sprt + Fant_arts + Won_arts + Time_art + Olympiad_Participation

# Create the decision tree model with the "information" splitting criterion
Hobby_Data_ctree <- rpart(myFormula, data = trainData, method = "class", parms = list(split = "information"))





# Print the decision tree
print(Hobby_Data_ctree)

# Plot the decision tree

rpart.plot(Hobby_Data_ctree)

Decision Tree Analysis Using Information gain(80/20):

In Second Tree ,we devide dataset into training set and test set with size(%80,%20) respectively. As you can see in the figure, the root node (“Career_sprt”) serves as the starting point for the classification process since have the heights Gain. The dataset has a distribution of approximately 43%for class1 (“Academics”), 26% for class 2(“Arts”), and 31% for class 3 (“Sports”).

The tree further branches based on the values of the “Career_sprt” if equal 0, tree branches based on”Won_arts” ,and majority of instances fall into “Acadimcs”, constituting 65% , if”Won_arts” equal 0 or2 In this case the tree terminates with a leaf node indicating a high probability 90% for “Acadimcs”.else the instances are classified based on “Fant_arts”into “Arts” with a probability 88%,if equal 0 the instances are classified into “Academics” with a probability of 72% ,else the instances are classified into “Arts” with a high probability 96%, if “Career_sprt” is 1, the tree further branches based on the “Fant_arts” into “Sports” with a probability 78%. If “Fant_arts” is 1 , there is another split based on the “Time_art” into “Arts” with a probability 50%. If “Time_art” is greater than or equal to 3, the instances are classified into “Arts” with a probability of 85% . On the other hand, if “Time_art” is less than 3, the instances are classified into “Sports” with a probability of 58% .If “Fant_arts” If not equal 1 , the instances are classified into “Sports” with a high probability 94%, as indicated by the leaf node.

Second Confusion matrix


# Predict on the test data
testPred <- predict(Hobby_Data_ctree, newdata = testData, type = 'class')

# Check the accuracy of the model
accuracy <- sum(testPred == testData$`Predicted Hobby`) / nrow(testData) * 100
cat('Accuracy:', accuracy, '\n')

# Create a confusion matrix
conf_matrix <- table(Actual = testData$`Predicted Hobby`, Predicted = testPred)

# Calculate precision for each class
precision_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
precision_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
precision_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])

# Calculate sensitivity for each class
sensitivity_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
sensitivity_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
sensitivity_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])

# Calculate specificity for each class
specificity_class_1 <- sum(diag(conf_matrix[-1, -1])) / sum(conf_matrix[-1, ])
specificity_class_2 <- sum(conf_matrix[c(1, 3), c(1, 3)]) / sum(conf_matrix[c(1, 3), ])
specificity_class_3 <- sum(conf_matrix[c(1, 2), c(1, 2)]) / sum(conf_matrix[c(1, 2), ])

# Calculate macro-average sensitivity
macro_avg_sensitivity <- (sensitivity_class_1 + sensitivity_class_2 + sensitivity_class_3) / 3

# Calculate macro-average specificity
macro_avg_specificity <- (specificity_class_1 + specificity_class_2 + specificity_class_3) / 3

# Calculate macro-average precision
macro_avg_precision <- (precision_class_1 + precision_class_2 + precision_class_3) / 3

# Print macro-average sensitivity
cat('Average Sensitivity:', macro_avg_sensitivity, '\n')

# Print macro-average specificity
cat('Average Specificity:', macro_avg_specificity, '\n')

# Print macro-average precision
cat('Average Precision:', macro_avg_precision, '\n')






# Print precision for each class
cat('Precision for Class 1:', precision_class_1, ' \n')

cat('Precision for Class 2:', precision_class_2, '  \n')
cat('Precision for Class 3:', precision_class_3, ' \n')

# Print sensitivity for each class
cat('Sensitivity for Class 1:', sensitivity_class_1, '\n')
cat('Sensitivity for Class 2:', sensitivity_class_2, '\n')
cat('Sensitivity for Class 3:', sensitivity_class_3, '\n')

# Print specificity for each class
cat('Specificity for Class 1:', specificity_class_1, '\n')
cat('Specificity for Class 2:', specificity_class_2, '\n')
cat('Specificity for Class 3:', specificity_class_3, '\n')

3-Information Gain(90%,10%)

library(rpart)

library(caTools)
library(rpart.plot)
library(caret)
# Set the seed for reproducibility
set.seed(1234)

# Split the data into training and testing sets
ind <- sample(2, nrow(Hobby_Data), replace=TRUE, prob=c(0.9, 0.1))
trainData <- Hobby_Data[ind == 1,]
testData <- Hobby_Data[ind == 2,]

# Define the formula for the decision tree
myFormula <- `Predicted Hobby` ~ Scholarship + Fav_sub + Projects + Grasp_pow + Time_sprt + Career_sprt + Act_sprt + Fant_arts + Won_arts + Time_art + Olympiad_Participation

# Create the decision tree model with the "information" splitting criterion
Hobby_Data_ctree <- rpart(myFormula, data = trainData, method = "class", parms = list(split = "information"))



# Print the decision tree
print(Hobby_Data_ctree)

# Plot the decision tree

rpart.plot(Hobby_Data_ctree)

Decision Tree Analysis Using Information gain(90/10):

In Third Tree ,we devide dataset into training set and test set with size(%90,%10) respectively. As you can see in the figure, the root node (“Career_sprt”) serves as the starting point for the classification process since have the heights Gain. The dataset has a distribution of approximately 43% for class 1(“Academics”), 25% for class 2(“Arts”), and 32% for class 3 (“Sports”).

The tree further branches based on the values of the “Career_sprt” if equal 0, tree branches based on”Won_arts” ,and majority of instances fall into “Acadimcs”, constituting 65% , if”Won_arts” equal 0 or 2 In this case the tree terminates with a leaf node indicating a high probability 90% for “Acadimcs”.else the instances are classified based on “Fant_arts”into “Arts” with a probability 87%,if equal 0 the instances are classified into “Academics” with a probability of 74% ,else the instances are classified into “Arts” with a high probability 96%, if “Career_sprt” is 1, the tree further branches based on the “Fant_arts” into “Sports” with a probability 79%. If “Fant_arts” is 1 , there is another split based on the “Time_art” into “Arts” with a probability 48%. If “Time_art” is greater than or equal to 3, the instances are classified into “Arts” with a probability of 84% . On the other hand, if “Time_art” is less than 3, the instances are classified based on”Act_sprt ” into “Sports” with a probability of 58% .if “Act_sprt” equal 0 the instances are classified into “Academics” with a probability of 59%, other hand the instances are classified into “Sports” with a probability of 78%. If “Fant_arts” not equal 1 , the instances are classified into “Sports” with a high probability 94%, as indicated by the leaf node.

Third Confusion matrix


# Predict on the test data
testPred <- predict(Hobby_Data_ctree, newdata = testData, type = 'class')

# Check the accuracy of the model
accuracy <- sum(testPred == testData$`Predicted Hobby`) / nrow(testData) * 100
cat('Accuracy:', accuracy, '\n')

# Create a confusion matrix
conf_matrix <- table(Actual = testData$`Predicted Hobby`, Predicted = testPred)

# Calculate precision for each class
precision_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
precision_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
precision_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])

# Calculate sensitivity for each class
sensitivity_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
sensitivity_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
sensitivity_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])

# Calculate specificity for each class
specificity_class_1 <- sum(diag(conf_matrix[-1, -1])) / sum(conf_matrix[-1, ])
specificity_class_2 <- sum(conf_matrix[c(1, 3), c(1, 3)]) / sum(conf_matrix[c(1, 3), ])
specificity_class_3 <- sum(conf_matrix[c(1, 2), c(1, 2)]) / sum(conf_matrix[c(1, 2), ])

# Calculate macro-average sensitivity
macro_avg_sensitivity <- (sensitivity_class_1 + sensitivity_class_2 + sensitivity_class_3) / 3

# Calculate macro-average specificity
macro_avg_specificity <- (specificity_class_1 + specificity_class_2 + specificity_class_3) / 3

# Calculate macro-average precision
macro_avg_precision <- (precision_class_1 + precision_class_2 + precision_class_3) / 3

# Print macro-average sensitivity
cat('Average Sensitivity:', macro_avg_sensitivity, '\n')

# Print macro-average specificity
cat('Average Specificity:', macro_avg_specificity, '\n')

# Print macro-average precision
cat('Average Precision:', macro_avg_precision, '\n')






# Print precision for each class
cat('Precision for Class 1:', precision_class_1, ' \n')

cat('Precision for Class 2:', precision_class_2, '  \n')
cat('Precision for Class 3:', precision_class_3, ' \n')

# Print sensitivity for each class
cat('Sensitivity for Class 1:', sensitivity_class_1, '\n')
cat('Sensitivity for Class 2:', sensitivity_class_2, '\n')
cat('Sensitivity for Class 3:', sensitivity_class_3, '\n')

# Print specificity for each class
cat('Specificity for Class 1:', specificity_class_1, '\n')
cat('Specificity for Class 2:', specificity_class_2, '\n')
cat('Specificity for Class 3:', specificity_class_3, '\n')

Comparing Decision Tree Results Using Infromation gain: After training three trees with different sizes, employing information gain as the selection measure, our analysis led to consistent accuracy results among the trees: Tree 1 (0.8932), Tree 2 (0.9057), and Tree 3 (0.8721). The minor discrepancies observed in these accuracy values could be attributed to the variations in dataset sizes. Investigating the impact of different training set sizes on model performance offers valuable insights into the intricate relationship between data size and accuracy.

In the case of Tree 2, where a larger training set was employed (80% training, 20% testing), the model had the opportunity to grasp more robust patterns and relationships within the data. However, it is crucial to underscore the necessity of striking a balance between the sizes of the training and testing sets. For Tree 3, with a relatively smaller testing set (90% training, 10% testing), the accuracy estimate might be less reliable due to the limited sample size in the testing set.

In summary, the utilization of information gain as the selection measure, coupled with different training set sizes, resulted in comparable accuracy outcomes. Achieving an optimal balance in the sizes of both training and testing datasets proves essential for ensuring accurate and generalizable model performance. +——————+——————-+——————-+——————-+ | information gain | 90 %t raining set | 80 %t raining set | 70 %t raining set | | | | | | | | 10% testing set: | 20% testing set: | 30% testing set: | +:================:+:=================:+:=================:+:=================:+ | Accuracy | 0.872 | 0.905 | 0.893 | +——————+——————-+——————-+——————-+ | precision | 0.856 | 0.898 | 0.887 | +——————+——————-+——————-+——————-+ | sensitivity | 0.856 | 0.898 | 0.887 | +——————+——————-+——————-+——————-+ | specificity | 0.919 | 0.937 | 0.929 | +——————+——————-+——————-+——————-+

Gini index

The Gini index, is a measure used in decision trees, specifically in the CART (Classification and Regression Trees) algorithm, to quantify how often a randomly chosen element would be incorrectly labeled if it was randomly labeled according to the distribution of labels in the subset. It reflects the probability of a particular variable being wrongly classified when it is randomly chosen.

1-Gini index(80%,20%)

Install necessary libraries

install.packages("rpart")
install.packages("rpart.plot")
install.packages("caTools")
install.packages("caret")

Load necessary libraries

library(rpart)
library(rpart.plot)
library(caTools)
library(caret)

Set a seed for reproducibility

set.seed(123)

Split the dataset, 80% for training, 20% for testing

split <- sample.split(Hobby_Data$`Predicted Hobby`, SplitRatio = 0.80)

Create the training set (80% of the data)

training_set <- subset(Hobby_Data, split == TRUE)

Create the test set (20% of the data)

test_set <- subset(Hobby_Data, split == FALSE)

Build a decision tree model on the training set

tree <- rpart(`Predicted Hobby` ~ ., data = training_set, method = 'class')

Make predictions on the test set using the tree model

predictions <- predict(tree, test_set, type = "class")

Confusion matrix

conf_matrix <- table(Predicted = predictions, Actual = test_set$`Predicted Hobby`)

Calculate accuracy

accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)

Initialize vectors to hold the metrics for each class

precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))

Calculate metrics for each class

for (i in 1:nrow(conf_matrix)) {
  TP <- conf_matrix[i, i]
  FP <- sum(conf_matrix[, i]) - TP
  FN <- sum(conf_matrix[i, ]) - TP
  TN <- sum(conf_matrix) - TP - FP - FN
  
  precision[i] <- TP / (TP + FP)
  recall[i] <- TP / (TP + FN)
  specificity[i] <- TN / (TN + FP)
}

Average the metrics if you want a single performance measure

avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)

Output the evaluation metrics

print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))

the metrics for each class:

metrics <- data.frame(
  Class = rownames(conf_matrix),
  Precision = precision,
  Recall = recall,
  Specificity = specificity
)

Print metrics

print(metrics)

Plot the decision tree

rpart.plot(tree)

Decision Tree Analysis Using Gini Index(80/20): The decision tree delineates hobbies into ‘Academics’ (1), ‘Arts’ (2), and ‘Sports’ (3). Without a sports hobby (‘Career_sprt’ = 0), the model suggests a 62% chance of ‘Academics’. With no arts hobby (‘Fant_arts’ = 0) and ‘Won_arts’ at 0 or 2, there’s a 43% chance of an ‘Academics’ categorization. Conversely, for those with an arts hobby (‘Fant_arts’ = 1) and frequent arts activities (‘Time_art’ ≥ 3), These model show how likely the model is to predict each hobby based on the attributes’ significance, as learned from the data with a 80% training portion

2-Gini index(90%,10%)

Install necessary libraries

install.packages("rpart")
install.packages("rpart.plot")
install.packages("caTools")
install.packages("caret")

Load necessary libraries

library(rpart)
library(rpart.plot)
library(caTools)
library(caret)

Set a seed for reproducibility

set.seed(123)

Split the dataset, 90% for training, 10% for testing

split <- sample.split(Hobby_Data$`Predicted Hobby`, SplitRatio = 0.90)

Create the training set (90% of the data)

training_set <- subset(Hobby_Data, split == TRUE)

Create the test set (10% of the data)

test_set <- subset(Hobby_Data, split == FALSE)

Build a decision tree model on the training set

tree <- rpart(`Predicted Hobby` ~ ., data = training_set, method = 'class')

Make predictions on the test set using the tree model

predictions <- predict(tree, test_set, type = "class")

Confusion matrix

conf_matrix <- table(Predicted = predictions, Actual = test_set$`Predicted Hobby`)

Calculate accuracy

accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)

Initialize vectors to hold the metrics for each class

precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))

Calculate metrics for each class

for (i in 1:nrow(conf_matrix)) {
  TP <- conf_matrix[i, i]
  FP <- sum(conf_matrix[, i]) - TP
  FN <- sum(conf_matrix[i, ]) - TP
  TN <- sum(conf_matrix) - TP - FP - FN
  
  precision[i] <- TP / (TP + FP)
  recall[i] <- TP / (TP + FN)
  specificity[i] <- TN / (TN + FP)
}

Average the metrics if you want a single performance measure

avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)

Output the evaluation metrics

print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))

the metrics for each class:

metrics <- data.frame(
  Class = rownames(conf_matrix),
  Precision = precision,
  Recall = recall,
  Specificity = specificity
)

Print metrics

print(metrics)

Plot the decision tree

rpart.plot(tree)

Decision Tree Analysis Using Gini Index(90/10):

The decision tree classifies hobbies into ‘Academics’ (1), ‘Arts’ (2), and ‘Sports’ (3). A lack of a sports hobby (‘Career_sprt’ = 0) leads to a 63% chance of falling into ‘Academics’. If someone is not engaged in an arts hobby (‘Fant_arts’ = 0) and ‘Won_arts’ is 0 or 2, there’s a 43% probability of an ‘Academics’ categorization. For individuals engaged in an arts hobby (‘Fant_arts’ = 1) with a high level of arts activity (‘Time_art’ ≥ 3), the likelihood of a ‘Sports’ classification is 28%. These model show how likely the model is to predict each hobby based on the attributes’ significance, as learned from the data with a 90% training portion.

3-Gini index(70%,30%)

Install necessary libraries

install.packages("rpart")
install.packages("rpart.plot")
install.packages("caTools")
install.packages("caret")

Load necessary libraries

library(rpart)
library(rpart.plot)
library(caTools)
library(caret)

Set a seed for reproducibility

set.seed(123)

Split the dataset, 70% for training, 30% for testing

split <- sample.split(Hobby_Data$`Predicted Hobby`, SplitRatio = 0.70)

Create the training set (70% of the data)

training_set <- subset(Hobby_Data, split == TRUE)

Create the test set (20% of the data)

test_set <- subset(Hobby_Data, split == FALSE)

Build a decision tree model on the training set

tree <- rpart(`Predicted Hobby` ~ ., data = training_set, method = 'class')

Make predictions on the test set using the tree model

predictions <- predict(tree, test_set, type = "class")

Confusion matrix

conf_matrix <- table(Predicted = predictions, Actual = test_set$`Predicted Hobby`)

Calculate accuracy

accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)

Initialize vectors to hold the metrics for each class

precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))

Calculate metrics for each class

for (i in 1:nrow(conf_matrix)) {
  TP <- conf_matrix[i, i]
  FP <- sum(conf_matrix[, i]) - TP
  FN <- sum(conf_matrix[i, ]) - TP
  TN <- sum(conf_matrix) - TP - FP - FN
  
  precision[i] <- TP / (TP + FP)
  recall[i] <- TP / (TP + FN)
  specificity[i] <- TN / (TN + FP)
}

Average the metrics if you want a single performance measure

avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)

Output the evaluation metrics

print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))

the metrics for each class:

metrics <- data.frame(
  Class = rownames(conf_matrix),
  Precision = precision,
  Recall = recall,
  Specificity = specificity
)

Print metrics

print(metrics)

Plot the decision tree

rpart.plot(tree)

Decision Tree Analysis Using Gini Index(70/30):

The decision tree sorts hobbies into ‘Academics’ (1), ‘Arts’ (2), and ‘Sports’ (3). A non-sports hobby (‘Career_sprt’ = 0) results in a 63% probability of an ‘Academics’ categorization. If ‘Fant_arts’ is 0 and ‘Won_arts’ is 0 or 2, there’s a 43% chance of being classified as ‘Academics’. Conversely, for those involved in an arts hobby (‘Fant_arts’ = 1) with significant arts activity (‘Time_art’ ≥ 3), the model indicates a 28% probability of a ‘Sports’ hobby. This decision tree demonstrates the likelihood of predicting each hobby based on the importance of the attributes, as determined from the data trained with a 70% portion.

Comparing Decision Tree Results Using Gini Index:

Across Three Training-Test Sizes: The results of the decision trees from the 90/10, 80/20, and 70/30 dataset splits, there is a consistent pattern: ‘Career_sprt’ is always the root node, and the subsequent splits on ‘Won_arts’ and ‘Fant_arts’ are the same across all trees. This consistency in tree structure and the probabilities for predicting ‘Academics’ and ‘Sports’ across different splits suggest a stable and robust model that is reliable regardless of the training set size.

the accuracies of three data splits reveals distinct outcomes: the (90,10) split leads with the highest accuracy at 0.91875, suggesting that a larger training portion is more effective in this case. The (70,30) split follows with an accuracy of 0.91060, showing strong performance even with a larger test set. However, the commonly used (80,20) split lags slightly behind, achieving an accuracy of 0.90625. This comparison highlights the impact of varying training and testing proportions on model accuracy.

Gini index 90 %t raining set 10% testing set: 80 %t raining set 20% testing set: 70 %t raining set 30% testing set:
Accuracy 0.91875 0.906 0.911
precision 0.91850 0.905 0.909
sensitivity 0.91927 0.909 0.912
specificity 0.9578 0.952 0.954

Gain Ratio The third criterion employed for building the decision tree is Gain Ratio. Gain Ratio stands out as a significant metric in decision tree algorithms, especially in scenarios involving categorical target variables. It normalizes the reduction in entropy by taking into account the potential information content of the feature. This normalization process makes Gain Ratio particularly suitable for datasets with categorical target variables. By factoring in the intrinsic information of a split, Gain Ratio effectively mitigates bias towards features with higher levels, ensuring a more balanced evaluation of different attributes.

1-Gain ratio(90%,10%)

Install necessary libraries

install.packages("C50")
install.packages("printr")
install.packages("caret")

Load necessary libraries


library(C50)
library(printr)
library(caret)

Set a seed for reproducibility

set.seed(1958)

Splitting the data into training and test sets

train_indices <- sample(1:nrow(Hobby_Data), 0.9 * nrow(Hobby_Data))
Hobby.train <- Hobby_Data[train_indices, ]
Hobby.test <- Hobby_Data[-train_indices, ]

Training the decision tree model

model <- C5.0(`Predicted Hobby` ~ ., data = Hobby.train, control = C5.0Control(CF = 0.01))

Making predictions on the test set

predictions <- predict(model, newdata = Hobby.test, type = 'class')

Create a confusion matrix from the predictions and actual values

conf_matrix <- table(Predicted = predictions, Actual = Hobby.test$`Predicted Hobby`)

Calculate and print the accuracy of the model

accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
print(paste('Accuracy on test data is:', accuracy))

Initialize vectors to hold the metrics for each class

precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))

Calculate metrics for each class

for (i in 1:nrow(conf_matrix)) {
  TP <- conf_matrix[i, i]
  FP <- sum(conf_matrix[, i]) - TP
  FN <- sum(conf_matrix[i, ]) - TP
  TN <- sum(conf_matrix) - TP - FP - FN
  
  precision[i] <- TP / (TP + FP)
  recall[i] <- TP / (TP + FN)
  specificity[i] <- TN / (TN + FP)
}

Average the metrics if you want a single performance measure

avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)

Output the evaluation metrics

print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))

print the metrics for each class:


metrics <- data.frame(
  Class = rownames(conf_matrix),
  Precision = precision,
  Recall = recall,
  Specificity = specificity
)

Print metrics

print(metrics)

Generate and print additional performance metrics using caret package

confusionMatrix(predictions, Hobby.test$`Predicted Hobby`)

Plot the decision tree

plot(model)

Decision Tree Analysis Using Gain Ratio(90%/10%):

In First Tree ,we devide dataset into training set and test set with size(%90,%10) respectively. As you can see in the figure, the root node is “Career_sprt” , class 1(“Academics”), class 2(“Arts”), and class 3 (“Sports”).

Node “Career_sprt” The first decision is based on whether the value of the “Career_sprt” attribute is 0. then check if “Won_arts” is either 0 or 2.If” Won_arts” is 0 or 2 , predict”Academic”.then check If “Fant_arts” is 1 “and Won_arts” is 1, predict “Arts”.If “Fant_arts” is 0 and Won_arts is 0 or 2, then check if Olympiad_Participation is 1.If Olympiad_Participation is 1 predict “Academics”.(When Olympiad_Participation is 0) If “Olympiad_Participation” is 0 and “Fant_arts” is 0, then check if “Grasp_pow” is less than or equal to 4 , predict class”Arts”.When Career_sprt is 1, then check if “Fant_arts” is 0 then predict “Sports”. If “Fant_arts” is 1, then check if “Time_art” is greater than 2.check if “Time_art” is less than or equal to 2, then check if “Act_sprt” is 1 or 0.If Act_sprt is 1, predict “Sports”. If Act_sprt is 0, then check if Olympiad_Participation is 0 predict “Arts”. If “Olympiad_Participation” is 1, predict “Academics”.When Time_art is greater than 2 ,If Won_arts is 0, predict class “Sports”.

2-Gain ratio(80%,20%)

Install necessary libraries

install.packages("C50")
install.packages("printr")
install.packages("caret")

Load necessary libraries


library(C50)
library(printr)
library(caret)

Set a seed for reproducibility

set.seed(1958)

Splitting the data into training and test sets

train_indices <- sample(1:nrow(Hobby_Data), 0.8 * nrow(Hobby_Data))
Hobby.train <- Hobby_Data[train_indices, ]
Hobby.test <- Hobby_Data[-train_indices, ]

Training the decision tree model

model <- C5.0(`Predicted Hobby` ~ ., data = Hobby.train, control = C5.0Control(CF = 0.01))

Making predictions on the test set

predictions <- predict(model, newdata = Hobby.test, type = 'class')

Create a confusion matrix from the predictions and actual values

conf_matrix <- table(Predicted = predictions, Actual = Hobby.test$`Predicted Hobby`)

Calculate and print the accuracy of the model

accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
print(paste('Accuracy on test data is:', accuracy))

Initialize vectors to hold the metrics for each class

precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))

Calculate metrics for each class

for (i in 1:nrow(conf_matrix)) {
  TP <- conf_matrix[i, i]
  FP <- sum(conf_matrix[, i]) - TP
  FN <- sum(conf_matrix[i, ]) - TP
  TN <- sum(conf_matrix) - TP - FP - FN
  
  precision[i] <- TP / (TP + FP)
  recall[i] <- TP / (TP + FN)
  specificity[i] <- TN / (TN + FP)
}

Average the metrics if you want a single performance measure

avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)

Output the evaluation metrics

print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))

print the metrics for each class:


metrics <- data.frame(
  Class = rownames(conf_matrix),
  Precision = precision,
  Recall = recall,
  Specificity = specificity
)

Print metrics

print(metrics)

Generate and print additional performance metrics using caret package

confusionMatrix(predictions, Hobby.test$`Predicted Hobby`)

Plot the decision tree

plot(model)

Decision Tree Analysis Using Gain Ratio(80/20):

The decision tree depicted classifies hobbies into ‘Academics’ (1), ‘Arts’ (2), and ‘Sports’ (3). It starts with ‘Career_sprt’ a value of 0 leads to ‘Won_arts’. If ‘Won_arts’ is 0 or 2, the model suggests ‘Academics’ or ‘Arts’. If ‘Career_sprt’ is 1, ‘Fant_arts’ is considered next; a value of 0 after ‘Won_arts’ being 1 points towards ‘Arts’, while a value of 1 leads to ‘Olympiad_Participation’, which, if 1, indicates ‘Academics’. Conversely, a high ‘Grasp_pow’ (>4) predicts ‘Sports’.

3-Gain ratio(70%,30%)

Install necessary libraries

install.packages("C50")
install.packages("printr")
install.packages("caret")

Load necessary libraries


library(C50)
library(printr)
library(caret)

Set a seed for reproducibility

set.seed(1958)

Splitting the data into training and test sets

train_indices <- sample(1:nrow(Hobby_Data), 0.7 * nrow(Hobby_Data))
Hobby.train <- Hobby_Data[train_indices, ]
Hobby.test <- Hobby_Data[-train_indices, ]

Training the decision tree model

model <- C5.0(`Predicted Hobby` ~ ., data = Hobby.train, control = C5.0Control(CF = 0.01))

Making predictions on the test set

predictions <- predict(model, newdata = Hobby.test, type = 'class')

Create a confusion matrix from the predictions and actual values

conf_matrix <- table(Predicted = predictions, Actual = Hobby.test$`Predicted Hobby`)

Calculate and print the accuracy of the model

accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
print(paste('Accuracy on test data is:', accuracy))

Initialize vectors to hold the metrics for each class

precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))

Calculate metrics for each class

for (i in 1:nrow(conf_matrix)) {
  TP <- conf_matrix[i, i]
  FP <- sum(conf_matrix[, i]) - TP
  FN <- sum(conf_matrix[i, ]) - TP
  TN <- sum(conf_matrix) - TP - FP - FN
  
  precision[i] <- TP / (TP + FP)
  recall[i] <- TP / (TP + FN)
  specificity[i] <- TN / (TN + FP)
}

Average the metrics if you want a single performance measure

avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)

Output the evaluation metrics

print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))

print the metrics for each class:


metrics <- data.frame(
  Class = rownames(conf_matrix),
  Precision = precision,
  Recall = recall,
  Specificity = specificity
)

Print metrics

print(metrics)

Generate and print additional performance metrics using caret package

confusionMatrix(predictions, Hobby.test$`Predicted Hobby`)

Plot the decision tree

plot(model)

Decision Tree Analysis Using Gain Ratio(70%/30%):

In Third Tree ,we devide dataset into training set and test set with size(%70,%30) respectively. As you can see in the figure, the root node is “Career_sprt” , class 1(“Academics”), class 2(“Arts”), and class 3 (“Sports”).

The first decision is based on whether the value of the “Career_sprt” attribute is 0.If “Career_sprt” is 0, then check if “Won_arts” is either 0 or 2.predict “Academics”.If “Won_arts” is 1, then check if “Fant_arts” is 1,predict “Arts”.If ““Fant_arts is 0 and”Won_arts” is 0 or 2, then check if “Time_art” is less than or equal to 2,predict “Academics”. If “Time_art” is greater than 2, predict “Arts”.

If “Career_sprt”is 1, then check if “Fant_arts” is 0, predict “Sports”.If “Fant_arts”is 1, then check if “Time_art” is less than or equal to 2 check if “Act_sprt” is 0,predict “Academics” .If “Act_sprt” is 1 predict “Sports”. if “Time_art” is greater than 2, then check if “Won_arts” is 0 predict “Sports”.If “Won_arts” is 1 or 2 predict “Arts”.

Comparing Decision Tree Results Using Gain Ratio

Across Three Training-Test Sizes: The accuracy rates -0.8944 for the 90:10 split, 0.8939 for the 70:30 split, and 0.8879 for the 80:20 split – indicate only slight variations, with the 90:10 split being marginally better.

Gain ratio

90 %t raining set

10% testing set:

80 %t raining set

20% testing set:

70 %t raining set

30% testing set:

Accuracy 0.8944 0.888 0.893970893970894
precision 0.892 0.881 0.890
sensitivity 0.891 0.890 0.902
specificity 0.945 0.943 0.947

————————————————-Clustering————————————————————-

str(Hobby_proc)
<<<<<<< Updated upstream =======
 num [1:1601, 1:12] 0.973 0.973 0.973 0.973 0.973 ...
 - attr(*, "dimnames")=List of 2
  ..$ : NULL
  ..$ : chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
 - attr(*, "scaled:center")= Named num [1:12] 0.513 0.417 2.112 0.578 3.48 ...
  ..- attr(*, "names")= chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
 - attr(*, "scaled:scale")= Named num [1:12] 0.5 0.493 1.05 0.494 1.007 ...
  ..- attr(*, "names")= chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
>>>>>>> Stashed changes

To convert type from factor columns to numeric


Hobby_proc$Fav_sub <-as.numeric(Hobby_proc$Fav_sub)

Hobby_proc$Olympiad_Participation <-as.numeric(Hobby_proc$Olympiad_Participation)

Hobby_proc$Projects <-as.numeric(Hobby_proc$Projects)

Hobby_proc$Scholarship <-as.numeric(Hobby_proc$Scholarship)

Hobby_proc$Career_sprt <-as.numeric(Hobby_proc$Career_sprt)

Hobby_proc$Act_sprt <-as.numeric(Hobby_proc$Act_sprt)

Hobby_proc$Fant_arts <-as.numeric(Hobby_proc$Fant_arts)

Hobby_proc$Won_arts <-as.numeric(Hobby_proc$Won_arts)

Hobby_proc$`Predicted Hobby` <-as.numeric(Hobby_proc$`Predicted Hobby` )
str(Hobby_proc)
<<<<<<< Updated upstream =======
 num [1:1601, 1:12] 0.973 0.973 0.973 0.973 0.973 ...
 - attr(*, "scaled:center")= Named num [1:12] 2.57e-17 6.36e-17 -1.29e-16 1.69e-17 1.72e-17 ...
  ..- attr(*, "names")= chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
 - attr(*, "scaled:scale")= Named num [1:12] 1 1 1 1 1 1 1 1 1 1 ...
  ..- attr(*, "names")= chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
 - attr(*, "dimnames")=List of 2
  ..$ : NULL
  ..$ : chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
>>>>>>> Stashed changes

Scaled All Columns

Hobby_proc <-scale(Hobby_proc)

Data set without ground truth

Hobby_Data2 <- Hobby_proc[, !(colnames(Hobby_proc) %in% c("Predict Hobby"))]

Our data set exhibits a balanced distribution among class labels, with “academic,” “art,” and “sport” constituting 43.6%, 25.6%, and 30.8% of the total, respectively. This balanced distribution is advantageous for both classification and clustering tasks. In classification, a balanced data set helps prevent the model from favoring one class over the others, ensuring that the learning algorithm is exposed to a representative set of examples from each category. This balance promotes the development of a model that generalizes well across all classes, enhancing its predictive performance on new, unseen data. In clustering, a balanced data set aids in forming clusters that are more evenly distributed, allowing for a comprehensive understanding of patterns and relationships across diverse categories. Balanced data sets often lead to more accurate and fair clustering results, enabling meaningful insights into the underlying structures within each class.

require packages/Library

install.packages("ggplot2") 
install.packages("magrittr")
install.packages("dplyr")
library(factoextra) 
library(cluster)
library(dplyr)

———————————————k-meansclustering————————————————

Validation:

Determining the right number of clusters before starting the clustering process is like making sure you have the correct-sized puzzle pieces before putting the puzzle together. It’s important because some clustering algorithms, like K-means, require such a parameter. In addition to that, it helps make the clustering more accurate and useful. If you know the right number beforehand, it saves time and helps make the whole process more efficient and the results more reliable.

compute average silhouette for k clusters using silhouette() For k-mean

silhouette_score <- function(k) {
  km <- kmeans(Hobby_Data2, centers = k, nstart = 25)
  ss <- silhouette(km$cluster, dist(Hobby_Data2))
  sil <- mean(ss[, 3])
  return(sil)
}

# k cluster range from 2 to 10
k <- 2:10

# call function for each k value
avg_sil <- sapply(k, silhouette_score)

# plot the results
plot(k, avg_sil, type = 'b', xlab = 'Number of clusters', ylab = 'Average Silhouette Scores', frame = FALSE)

The results indicate that the highest average silhouette scores indicate the quality of the clusters and suggest better-defined and more separated clusters, with each point having a high degree of similarity to its own cluster and a lower similarity to neighboring clusters. The highest average silhouette scores were observed for k values of 3, 2, and 4, so these are the optimal numbers. These values will be employed in subsequent k-means clustering analyses.

k-means cluster k=3

#set a seed for random number generation  to make the results reproducible
set.seed(7)
kmeans.result <- kmeans(Hobby_Data2, 3)

# print the clusterng result
kmeans.result

#visualize clustering
fviz_cluster(kmeans.result, data = Hobby_Data2)

Average for each cluster

avg_sil <- silhouette(kmeans.result$cluster,dist(Hobby_Data2)) 
fviz_silhouette(avg_sil)

the presence of negative silhouettes for some observations within each cluster indicates that these points might be more similar to points in other clusters, suggesting a potential overlap or ambiguity in their assignment. The fact that some observations have negative silhouettes highlights that the separation of data points is not entirely sufficient.

So since 3, which was the optimal number of clusters with the highest silhouette score average, did not have good clustering results, that does support our research results that the k-means algorithm is not applicable to categoricaldata clustering because it relies on the Euclidean distance metric tomeasure the similarity between data points. However, even afterEncoding and its application to categorical data pose significant challenges. Categorical variables often lack a meaningful numericalrepresentation; for instance, taking the mean of categories like thefeature “favorite subject,” aka Fav_sub” (even after encoding), might nothave any practical interpretation. And the distances calculated in thealgorithm may not reflect the true dissimilarities between categoricalvalues. The encoding process itself introduces artificial numericalrelationships that may mislead the algorithm. Moreover, k-means relieson the minimization of Euclidean distances, which might not accuratelycapture the dissimilarity structure in categorical data. Categoricalvariables inherently exhibit discrete and non-ordinal characteristicsthat are not well-suited for the continuous and linear assumptions ofk-means. Alternative clustering techniques, specifically designed forcategorical data, such as partitioning around medoids, are moreappropriate for capturing the intrinsic patterns and relationships incategorical datasets.

————————————————–k-mediods clustering with PAM——————————————–

K-medoids clustering presents a robust alternative for analyzing categorical data by addressing the limitations posed by k-means clustering. Unlike k-means, k-medoids does not rely on the mean as a representative centroid but employs medoids, which are actual data points within the clusters, and the algorithm defines clusters based on partitioning around medoids. This feature makes k-medoids particularly suitable for categorical data, where meaningful centroids may not have a numerical interpretation. The algorithm defines clusters based on partitioning around medoids.

Validation:

First, we want to determine three different numbers of clusters by using a number of methods that will suggest the optimal number of clusters for k-mediods clustering with PAM.

Silhouette coefficient

fviz_nbclust(Hobby_Data2, pam, method = "silhouette")+
  labs(subtitle = "Silhouette method")

Elbow method

fviz_nbclust(Hobby_Data2, pam, method = "wss") +
  geom_vline(xintercept= 3, linetype= 3)+
  labs(subtitle = "Elbow method")

The silhouette coefficient, which measures the cohesion and separation of clusters, aligns with the Elbow method, which assesses the within-cluster sum of squares (wss) as a function of the number of clusters in the suggested optimal number of clusters. This alignment in results between two distinct evaluation methods strengthens confidence in the choice of three clusters, providing a stable foundation for further analysis and interpretation of the underlying patterns within the dataset. In addition to that, the ground truth (class labels) also contains three classes, and that indicates a reassuring alignment between the structure of the data and the clustering results.

We need to determine two more suggested numbers of clusters by computing the average silhouette for k clusters using silhouette().

silhouette_score <- function(k) {
  km <- pam(Hobby_Data2, k, diss = TRUE)
  ss <- silhouette(km$clustering, dist(Hobby_Data2))
  sil <- mean(ss[, 3])
  return(sil)
}

# k cluster range from 2 to 10
k <- 2:10

# Call function for each k value
avg_sil <- sapply(k, silhouette_score)

# Plot the results
plot(k, avg_sil, type = 'b', xlab = 'Number of clusters', ylab = 'Average Silhouette Scores', frame = FALSE)

It is a common practice to choose the number of clusters corresponding to the peak in the silhouette score plot, and since we are looking for two more number of clusters other than three, it would be reasonable to consider two and four clusters for further analysis. Especially with the decreasing trend beyond three clusters, it indicates that adding more clusters does not significantly improve the separation and cohesion of the clusters.

————————————————–group into k=3 clusters——————————————–

The sub-sampling and clustering approach is a helpful method to evaluate the robustness of the clustering results under various subsets and to obtain insights into the structure of the data. Furthermore, we will take 100 samples from our data set to ensure that the clustering plot doesn’t get too crowded.

set.seed(7)

# Specify the number of rows you want to sample
num_rows <- 100

# Use sample with the specified seed
idx <- sample(1:dim(Hobby_Data2)[1], num_rows)

Hobby_Data3 <- Hobby_Data2[idx, ]

pam.result <- pam(Hobby_Data3,3)
#Show the silhoutee plot of PAM AND clusters
plot(pam.result)
<<<<<<< Updated upstream =======

>>>>>>> Stashed changes
# Extract the clusinfo component
clusinfo <- pam.result$clusinfo

# Calculate the total within-cluster sum of squares
tot_withinss <- sum(clusinfo[, "size"] * clusinfo[, "av_diss"]^2)

# Print the result
print(tot_withinss)
<<<<<<< Updated upstream =======
str(Hobby_Data2)
 num [1:1601, 1:11] 0.973 0.973 0.973 0.973 0.973 ...
 - attr(*, "dimnames")=List of 2
  ..$ : NULL
  ..$ : chr [1:11] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...

Data set without ground truth

Hobby_Data2 <- Hobby_proc[, !(colnames(Hobby_proc) %in% c("Predicted Hobby"))]
summary(Hobby_Data2)
 Olympiad_Participation  Scholarship         Fav_sub           Projects         Grasp_pow         Time_sprt         Career_sprt         Act_sprt      
 Min.   :-1.0269        Min.   :-0.8459   Min.   :-1.0586   Min.   :-1.1709   Min.   :-2.4637   Min.   :-1.50901   Min.   :-0.7698   Min.   :-1.1199  
 1st Qu.:-1.0269        1st Qu.:-0.8459   1st Qu.:-1.0586   1st Qu.:-1.1709   1st Qu.:-0.4771   1st Qu.:-0.76702   1st Qu.:-0.7698   1st Qu.:-1.1199  
 Median : 0.9732        Median :-0.8459   Median :-0.1065   Median : 0.8535   Median :-0.4771   Median :-0.02503   Median :-0.7698   Median : 0.8924  
 Mean   : 0.0000        Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.00000   Mean   : 0.0000   Mean   : 0.0000  
 3rd Qu.: 0.9732        3rd Qu.: 1.1815   3rd Qu.: 0.8457   3rd Qu.: 0.8535   3rd Qu.: 0.5162   3rd Qu.: 0.71696   3rd Qu.: 1.2981   3rd Qu.: 0.8924  
 Max.   : 0.9732        Max.   : 1.1815   Max.   : 1.7978   Max.   : 0.8535   Max.   : 2.5028   Max.   : 2.20095   Max.   : 1.2981   Max.   : 0.8924  
   Fant_arts          Won_arts          Time_art      
 Min.   :-0.8733   Min.   :-0.8648   Min.   :-0.9748  
 1st Qu.:-0.8733   1st Qu.:-0.8648   1st Qu.:-0.9748  
 Median :-0.8733   Median :-0.8648   Median :-0.1850  
 Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000  
 3rd Qu.: 1.1443   3rd Qu.: 0.4042   3rd Qu.: 0.6048  
 Max.   : 1.1443   Max.   : 1.6733   Max.   : 2.9741  
str(Hobby_Data2)
List of 17612
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
  [list output truncated]

# Assuming 'pam.result' is the result from PAM clustering
Hobby_Data2$cluster <- as.factor(pam.result$clustering)
Warning: Coercing LHS to a list

# Make sure 'cluster' is a factor
Hobby_Data2$cluster <- as.factor(Hobby_Data2$cluster)

# Check the structure of Hobby_Data2
str(Hobby_Data2)
List of 17612
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
 $        : num 0.973
  [list output truncated]
# Create parallel coordinate plot
p <- ggparcoord(
  data = Hobby_Data2,
  columns = c(1:11),  # Assuming there are 11 columns in your data
  groupColumn = "cluster",
  scale = "std"
) + labs(x = "milk constituent", y = "value (in standard-deviation units)", title = "Clustering")

# Convert the ggplot object to plotly
p_plotly <- ggplotly(p)

# Print the plot
print(p_plotly)
NULL
>>>>>>> Stashed changes

The output of the code, displaying a silhouette plot of the PAM clusters, indicates that the clusters are relatively close to each other, with a slight overlap between two clusters. The silhouette plot visually represents how well-defined and separated the clusters are. While the slight overlap suggests that the natural grouping within the dataset may not be entirely distinct, it does not negatively affect the overall quality of the clusters. In fact, the observed overlap might indicate shared characteristics between adjacent clusters, effectively capturing meaningful patterns and groupings that reflect the intricacies of real-world phenomena not confined to strict boundaries.

bCubed

cluster_assignments <- c(pam.result$cluster)
set.seed(7)

# Specify the number of rows you want to sample
num_rows <- 100

# Use sample with the specified seed
idx <- sample(1:dim(Hobby_proc)[1], num_rows)

# Select the sampled rows from Hobby_proc
Hobby_Data4 <- Hobby_proc[idx, ]
ground_truth_labels <- c(Hobby_Data4)

# Create a data frame with cluster assignments and ground truth labels
dataset <- data.frame(cluster = cluster_assignments, label = ground_truth_labels)

# Calculate BCubed precision and recall
calculate_bcubed_metrics <- function(dataset) {
  n <- nrow(dataset)
  precision_sum <- 0 
  recall_sum <- 0
  
   for (i in 1:n) {
    cluster <- dataset$cluster[i] 
    label <- dataset$label[i]
    
    # Count the number of items from the same category in its cluster
    same_category <- sum(dataset$label[dataset$cluster == cluster] == label)   
    
    # Count the number of items in its cluster    
    same_cluster <- sum(dataset$cluster == cluster)
    
    # Count the number of items in its category
    total_same_category <- sum(dataset$label == label)   
    
    # Calculate precision and recall 
    precision_sum <- precision_sum + same_category / same_cluster
    recall_sum <- recall_sum + same_category / total_same_category 
    }
  # End loop 
  
  # Calculate average precision and recall  
  precision <- precision_sum / n
  recall <- recall_sum / n 
  return(list(precision = precision, recall = recall))}

  # Calculate BCubed precision and recall
  metrics <- calculate_bcubed_metrics(dataset)
  precision <- metrics$precision
  recall <- metrics$recall

# Print the results
  cat("BCubed Precision= ", precision, "AND BCubed Recall= ", recall, "\n")

While precision highlights room for better accuracy in identifying similar items, the higher recall indicates the algorithm’s capability to catch a good amount of actual similarities within clusters.

————————————————–group into k=4 clusters————————————————

set.seed(7)

# Specify the number of rows you want to sample
num_rows <- 100

# Use sample with the specified seed
idx <- sample(1:dim(Hobby_Data2)[1], num_rows)

Hobby_Data3 <- Hobby_Data2[idx, ]

pam.result <- pam(Hobby_Data3,4)
#Show the silhoutee plot of PAM AND clusters
plot(pam.result)
# Extract the clusinfo component
clusinfo <- pam.result$clusinfo

# Calculate the total within-cluster sum of squares
tot_withinss <- sum(clusinfo[, "size"] * clusinfo[, "av_diss"]^2)

# Print the result
print(tot_withinss)

The output suggests that the dataset may exhibit a degree of overlap or similarity among observations. The overlapping clusters may indicate challenges in achieving a clear separation among these groups. The placement of one cluster on top of two others implies that the medoid of this cluster might be near points belonging to those two neighboring clusters. This could be due to the nature of the data.

cluster_assignments <- pam.result$clustering

# Add cluster assignments to the original data
Hobby_Data_with_clusters <- cbind(Hobby_Data3, Cluster = factor(cluster_assignments))

# Example: Pair plot for the first four features
pairs(Hobby_Data_with_clusters[, 1:4], col = cluster_assignments)

BCubed

cluster_assignments <- c(pam.result$cluster) 
 
set.seed(7) 
 
# Specify the number of rows you want to sample 
num_rows <- 100 
 
# Use sample with the specified seed 
idx <- sample(1:dim(Hobby_proc)[1], num_rows) 
 
# Select the sampled rows from Hobby_proc 
Hobby_Data4 <- Hobby_proc[idx, ] 

# Create a data frame with cluster assignments and ground truth labels
dataset <- data.frame(cluster = cluster_assignments, label = ground_truth_labels)

# Calculate BCubed precision and recall
calculate_bcubed_metrics <- function(dataset) {
  n <- nrow(dataset)
  precision_sum <- 0
  recall_sum <- 0
 
  for (i in 1:n) {
    cluster <- dataset$cluster[i]
    label <- dataset$label[i]
   
    # Count the number of items from the same category in its cluster
    same_category <- sum(dataset$label[dataset$cluster == cluster] == label)
   
    # Count the number of items in its cluster
    same_cluster <- sum(dataset$cluster == cluster)
   
    # Count the number of items in its category
    total_same_category <- sum(dataset$label == label)
   
    # Calculate precision and recall
    precision_sum <- precision_sum + same_category / same_cluster
    recall_sum <- recall_sum + same_category / total_same_category
  }
  # End loop
 
  # Calculate average precision and recall
  precision <- precision_sum / n
  recall <- recall_sum / n
 
  return(list(precision = precision, recall = recall))
}

# Calculate BCubed precision and recall
metrics <- calculate_bcubed_metrics(dataset)
precision <- metrics$precision
recall <- metrics$recall

# Print the results
cat("BCubed Precision= ", precision, "AND BCubed Recall= ", recall, "\n")

The results indicate challenges in clustering performance. The low precision suggests a significant rate of misclassification, while the relatively low recall indicates that some instances within the same group are missed or incorrectly assigned to other clusters. These results highlight limitations in accurately capturing the data’s underlying structure.

———————————————-group into k=2 clusters——————————————–

set.seed(7)

# Specify the number of rows you want to sample
num_rows <- 100

# Use sample with the specified seed
idx <- sample(1:dim(Hobby_Data2)[1], num_rows)

Hobby_Data3 <- Hobby_Data2[idx, ]

pam.result <- pam(Hobby_Data3,2)
#Show the silhoutee plot of PAM AND clusters
plot(pam.result)
# Extract the clusinfo component
clusinfo <- pam.result$clusinfo

# Calculate the total within-cluster sum of squares
tot_withinss <- sum(clusinfo[, "size"] * clusinfo[, "av_diss"]^2)

# Print the result
print(tot_withinss)

An overlap between clusters implies that there is ambiguity in the assignment of data points to clusters, and the clusters may not be sufficiently distinct. In such cases, it might be needed to reconsider the number of clusters since the goal is to find a balance, but too few clusters result in oversimplification, as indicated by the observed overlap, and it is evident that forming only two clusters may not be sufficient to represent the inherent structure of the dataset.

#BCubed

cluster_assignments <- c(pam.result$cluster) 
 
set.seed(7) 
 
# Specify the number of rows you want to sample 
num_rows <- 100 
 
# Use sample with the specified seed 
idx <- sample(1:dim(Hobby_proc)[1], num_rows) 
 
# Select the sampled rows from Hobby_proc 
Hobby_Data4 <- Hobby_proc[idx, ] 

# Create a data frame with cluster assignments and ground truth labels
dataset <- data.frame(cluster = cluster_assignments, label = ground_truth_labels)

# Calculate BCubed precision and recall
calculate_bcubed_metrics <- function(dataset) {
  n <- nrow(dataset)
  precision_sum <- 0
  recall_sum <- 0
 
  for (i in 1:n) {
    cluster <- dataset$cluster[i]
    label <- dataset$label[i]
   
    # Count the number of items from the same category in its cluster
    same_category <- sum(dataset$label[dataset$cluster == cluster] == label)
   
    # Count the number of items in its cluster
    same_cluster <- sum(dataset$cluster == cluster)
   
    # Count the number of items in its category
    total_same_category <- sum(dataset$label == label)
   
    # Calculate precision and recall
    precision_sum <- precision_sum + same_category / same_cluster
    recall_sum <- recall_sum + same_category / total_same_category
  }
  # End loop
 
  # Calculate average precision and recall
  precision <- precision_sum / n
  recall <- recall_sum / n
 
  return(list(precision = precision, recall = recall))
}

# Calculate BCubed precision and recall
metrics <- calculate_bcubed_metrics(dataset)
precision <- metrics$precision
recall <- metrics$recall

# Print the results
cat("BCubed Precision= ", precision, "AND BCubed Recall= ", recall, "\n")

The relatively high recall could be influenced by the specific choice of two clusters since it is sensitive to the number of clusters. In the context of a two-cluster solution, the recall score reflects how well the algorithm groups data points from the same class into one of the two identified clusters. Recall suggests that a significant portion of data points from the same ground truth class are indeed grouped together in one of the two clusters. However, it’s important to note that the low precision score (0.0432) indicates a lack of homogeneity within the identified clusters, implying that the clusters contain a mix of data points from different ground truth classes.

AS A Summary For clustring Silhouette analysis measures how similar an object is to its own cluster (cohesion) compared to other clusters (separation). The silhouette width ranges from -1 to 1, where a high value indicates that the object is well matched to its own cluster and poorly matched to neighboring clusters. In our project we did clusters for k=3, k=4, and k=2 since its have higher average silhouette then other number.

For k=3, the average silhouette width is 0.22. For k=4, the average silhouette width is 0.19. For k=2, the average silhouette width is 0.21.

A higher average silhouette width generally indicates better-defined clusters. So, in this case, k=3 has the highest average silhouette width.

BCubed is a clustering evaluation metric that considers both precision and recall. Precision measures the accuracy of the positive predictions, while recall measures the coverage of the actual positive instances.

For k=3, BBCubed Precision = 0.0488 BCubed Recall = 0.4932 For k=4,BCubed Precision = 0.0505 BCubed Recall = 0.4101 For k=2, BCubedPrecision = 0.0432 BCubed Recall = 0.6252

These metrics measure how well the clustering aligns with the ground truth. Higher precision indicates fewer false positives, and higher recall indicates fewer false negatives. Here, k=2 has the highest recall (0.6252), but k=3 has a reasonable balance between precision and recall.

In conclusion, k=3 seems to be a reasonable choice. It has a good silhouette width, and its BCubed Precision and Recall values strike a balance.

library(plotly)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

Findings

90%/10% 90%/10% 90%/10% 80%/20% 80%/20% 80%/20% 70%/30% 70%/30% 70%/30%
IG IG ratio Gini Index IG IG ratio Gini Index IG IG ratio Gini Index
Accuracy 0.872 0.8944 0.91875 0.905 0.888 0.906 0.893 0.8939 0.911
precision 0.856 0.892 0.91850 0.898 0.881 0.905 0.887 0.890 0.909
sensitiviy 0.856 0.891 0.91927 0.898 0.890 0.909 0.887 0.902 0.912
specificiy 0.919 0.945 0.9578 0.937 0.943 0.952 0.929 0.947 0.954

References

What is the best for our dataset?

since We used the Information Gain, Gini Index, and Gain Ratio as three important metrics in our categorical data classification approach to determine how important certain variables were in predicting hobby categories. The dataset was split into training and testing sets so that different subsets of the data could be used to train classification algorithms. Three metrics were used to evaluate the performance of the model: overall accuracy, recall, and precision.We examined accuracy at several measures of tree selection, Provide us with more accurate results, specifically at the pruning parameters of 0.90, 0.91, and 0.89, in order.The methodical evaluation yielded insightful information on how well each selection measure performed in producing precise predictions within the dataset. high accuracy strongly suggest that, for our dataset, classification is a better methodology than clustering, since the classification give us a good result then clustring.

<<<<<<< Updated upstream
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyAqKktpZHMgSG9iYnkgUHJlZGljdGlvbiBEYXRhc2V0KioNCg0KDQoqKlByb2JsZW0qKg0KDQpDaGlsZHJlbiBvZnRlbiBmaW5kIHRoZW1zZWx2ZXMgYXQgYSBjcm9zc3JvYWRzIHdoZW4gaXQgY29tZXMgdG8gZGlzY292ZXJpbmcgdGhlaXIgcGFzc2lvbnMgb3IgaG9iYmllcywgYmUgaXQgaW4gYWNhZGVtaWNzLCBhcnRzLCBvciBzcG9ydHMuIFJlY29nbml6aW5nIHRoZSBpbXBvcnRhbmNlIG9mIGd1aWRpbmcgY2hpbGRyZW4gdG93YXJkcyBhY3Rpdml0aWVzIHRoZXkgYXJlIHBhc3Npb25hdGUgYWJvdXQ7IHdlIGhhdmUgY3VyYXRlZCBhIGRhdGFzZXQgb2J0YWluZWQgdGhyb3VnaCBzdXJ2ZXlzIGNvbmR1Y3RlZCB3aXRoIHBhcmVudHMuIFRoaXMgZGF0YXNldCBjb21waWxlcyB2YWx1YWJsZSBpbmZvcm1hdGlvbiBhYm91dCBjaGlsZHJlbidzIHByZWZlcmVuY2VzLCBlbmFibGluZyB0aGUgY3JlYXRpb24gb2YgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbCBhaW1lZCBhdCBwcmVkaWN0aW5nIGtpZHMnIGhvYmJpZXMuIA0KDQpJbiB0aGlzIGV4cGxvcmF0b3J5IGpvdXJuZXksIHdlIGRlbHZlIGludG8gdGhlIGRhdGFzZXQgdG8gdW5jb3ZlciBwYXR0ZXJucyBhbmQgaW5zaWdodHMgdGhhdCBjYW4gYXNzaXN0IHBhcmVudHMgaW4gdW5kZXJzdGFuZGluZyB0aGVpciBjaGlsZCdzIGluY2xpbmF0aW9ucyBiZXR0ZXIuIFRocm91Z2ggdGhlIGFwcGxpY2F0aW9uIG9mIGNsdXN0ZXJpbmcgdGVjaG5pcXVlcywgd2UgYWltIHRvIGNhdGVnb3JpemUgY2hpbGRyZW4gYmFzZWQgb24gbnVtYmVyIG9mIGNvbHVtbnMgKEZhdl9zdWIgLCBTY2hvbGFyc2hpcCwgZXRjLi4pLiBUaGUgdWx0aW1hdGUgZ29hbCBpcyB0byBwcm92aWRlIHBhcmVudHMgd2l0aCBtZWFuaW5nZnVsIHJlY29tbWVuZGF0aW9ucywgZm9zdGVyaW5nIGFuIGVudmlyb25tZW50IHdoZXJlIGNoaWxkcmVuIGNhbiB0aHJpdmUgaW4gYWN0aXZpdGllcyB0aGF0IGdlbnVpbmVseSByZXNvbmF0ZSB3aXRoIHRoZWlyIGludGVyZXN0cy4gDQoNCkJ5IGFuYWx5emluZyB0aGlzIGRhdGFzZXQsIHdlIGhvcGUgdG8gb2ZmZXIgaW5zaWdodGZ1bCBpbmZvcm1hdGlvbiB0aGF0IHdpbGwgYWlkIHBhcmVudHMgaW4gZ3VpZGluZyB0aGVpciBraWRzIHRvd2FyZCB0aGUgcmlnaHQgaG9iYmllcy4gDQoNCg0KKipEYXRhIE1pbmluZyBUYXNrKioNCg0KVGhlIGRhdGEgbWluaW5nIHRhc2sgYXQgaGFuZCByZXZvbHZlcyBhcm91bmQgcHJlZGljdGluZyBraWRzJyBob2JiaWVzIGJhc2VkIG9uIGEgZGF0YXNldCBuYW1lZCAiSG9iYnlfRGF0YSwiIG9idGFpbmVkIHRocm91Z2ggc3BlY2lmaWMgcXVlc3Rpb25zIHBvc2VkIHRvIHRoZWlyIHBhcmVudHMgcmVnYXJkaW5nIHByZWZlcmVuY2VzLCBjYXBhYmlsaXRpZXMsIGFuZCBhY2hpZXZlbWVudHMuIFRoaXMgZGF0YXNldCBzZXJ2ZXMgYXMgdGhlIGZvdW5kYXRpb24gZm9yIHR3byBwcmltYXJ5IGRhdGEgbWluaW5nIHRhc2tzOiBjbGFzc2lmaWNhdGlvbiBhbmQgY2x1c3RlcmluZy4gSW4gdGhlIGNsYXNzaWZpY2F0aW9uIHByb2Nlc3MsIHRoZSBnb2FsIGlzIHRvIHRyYWluIGEgbWFjaGluZSBsZWFybmluZyBtb2RlbCB0byBiZSBjYXBhYmxlIG9mIGFjY3VyYXRlbHkgcHJlZGljdGluZyBhIGNoaWxkJ3MgaG9iYnkgYXMgZWl0aGVyICJhY2FkZW1pYywiICJhcnQsIiBvciAic3BvcnRzLiIgVGhpcyByZXF1aXJlcyB1c2luZyB0aGUgZGF0YSBjb2xsZWN0ZWQgZnJvbSBwYXJlbnRzIHRvIGVzdGFibGlzaCBwYXR0ZXJucyBhbmQgcmVsYXRpb25zaGlwcyB0aGF0IGd1aWRlIHRoZSBtb2RlbCBpbiBtYWtpbmcgYWNjdXJhdGUgcHJlZGljdGlvbnMuIENvbmN1cnJlbnRseSwgdGhlIGNsdXN0ZXJpbmcgcHJvY2VzcyBpbnZvbHZlcyBwYXJ0aXRpb25pbmcgdGhlIGRhdGFzZXQgaW50byBtZWFuaW5nZnVsIGNsdXN0ZXJzLCBncm91cGluZyB0b2dldGhlciBjaGlsZHJlbiB3aXRoIHNpbWlsYXIgY2hhcmFjdGVyaXN0aWNzIG9yIHByZWZlcmVuY2VzLiBUaHJvdWdoIHRoZXNlIGR1YWwgYXBwcm9hY2hlcywgdGhlIGRhdGEgbWluaW5nIHRhc2sgYWltcyB0byBkZXZlbG9wIGEgcm9idXN0IHByZWRpY3RpdmUgbW9kZWwgY2FwYWJsZSBvZiBhY2N1cmF0ZWx5IGNhdGVnb3JpemluZyBjaGlsZHJlbidzIGhvYmJpZXMgaW4gb3JkZXIgdG8gdW5jb3ZlciB0aGUgaW5oZXJlbnQgc3RydWN0dXJlcywgcGF0dGVybnMsIGFuZCBhc3NvY2lhdGlvbnMgd2l0aGluIHRoZSBkYXRhc2V0LCBjb250cmlidXRpbmcgdG8gYSBkZWVwZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGl2ZXJzZSBpbnRlcmVzdHMgYW5kIGVuZ2FnZW1lbnQgbGV2ZWxzIG9mIHRoZSB5b3VuZyBwb3B1bGF0aW9uIGluIGFjYWRlbWljLCBhcnRpc3RpYywgYW5kIHNwb3J0cy1yZWxhdGVkIGFjdGl2aXRpZXMuDQogDQogDQoNCg0KKipEYXRhKioNCg0KVGhlIHNvdXJjZTogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9hYnRhYm0vaG9iYnktcHJlZGljdGlvbi1iYXNpYyANCk51bWJlciBvZiBvYmplY3RzOiAxNjAxIA0KDQpOdW1iZXIgb2YgYXR0cmlidXRlczogMTQgDQoNCg0KKipEZXNjcmlwdGlvbjoqKg0KRm9sbG93aW5nIHRoZSBzZWxlY3Rpb24gb2Ygb3VyIGRhdGEgc2V0ICgiSG9iYnlfRGF0YSIpIHdoaWNoIHByZWRpY3RzIGtpZHMnIGhvYmJpZXMsIHRoYXQgd2FzIGNvbGxlY3RlZCBieSBhc2tpbmcgdGhlaXIgcGFyZW50cyBzcGVjaWZpYyBxdWVzdGlvbnMgYWJvdXQgdGhlaXIga2lkJ3MgcHJlZmVyZW5jZXMsIGNhcGFiaWxpdGllcywgYW5kIGFjaGlldmVtZW50cy4gVG8gaGVscCB1cyB0cmFpbiB0aGUgbWFjaGluZSB0byBwcmVkaWN0IHRoZSBraWQncyBob2JieS4gV2Ugd2lsbCBiZWdpbiB0byBwcmVwcm9jZXNzIGFuZCBhbmFseXplIHRoZSBkYXRhLg0KDQoNCg0KKipBdHRyaWJ1dGUqKiAqKkRlc2NyaXB0aW9uOioqDQoNCnwgQXR0cmlidXRlIG5hbWUgICAgICAgICB8ICoqRGVzY3JpcHRpb24qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAqKkRhdGEgdHlwZSoqIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLXwNCnwgT2x5bXBpYWRfUGFydGljaXBhdGlvbiB8IEhhcyB5b3VyIGNoaWxkIHBhcnRpY2lwYXRlZCBpbiBhbnkgU2NpZW5jZS9NYXRocyAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgU2Nob2xhcnNoaXAgICAgICAgICAgICB8IEhhcyBoZS9zaGUgcmVjZWl2ZWQgYW55IHNjaG9sYXJzaGlwPyAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgU2Nob29sICAgICAgICAgICAgICAgICB8IExvdmUncyBnb2luZyB0byBzY2hvb2w/ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgRmF2X3N1YiAgICAgICAgICAgICAgICB8IFdoYXQgaXMgaGlzL2hlciBmYXZvcml0ZSBzdWJqZWN0PyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBDYXRlZ29yaWNhbCAgIHwNCnwgUHJvamVjdHMgICAgICAgICAgICAgICB8IEhhcyBkb25lIGFueSBwcm9qZWN0cyB1bmRlciBhY2FkZW1pY3MgYmVmb3JlPyAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgR3Jhc3BfcG93ICAgICAgICAgICAgICB8IEhpcy9IZXIgR3Jhc3BpbmcgcG93ZXIgKDEtNikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgVGltZV9zcHJ0ICAgICAgICAgICAgICB8IEhvdyBtdWNoIHRpbWUgZG9lcyBoZS9zaGUgc3BlbmQgcGxheWluZyBvdXRkb29yL2luZG9vciBnYW1lcz8gfCBPcmRpbmFsICAgICAgIHwNCnwgTWVkYWxzICAgICAgICAgICAgICAgICB8IE1lZGFscyB3b24gaW4gU3BvcnRzPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgQ2FyZWVyX3NwcnQgICAgICAgICAgICB8IFdhbnQncyB0byBwdXJzdWUgaGlzL2hlciBjYXJlZXIgaW4gc3BvcnRzPyAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgQWN0X3NwcnQgICAgICAgICAgICAgICB8IFJlZ3VsYXIgaW4gaGlzL2hlciBzcG9ydHMgYWN0aXZpdGllcz8gICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgRmFudF9hcnRzICAgICAgICAgICAgICB8IExvdmUgY3JlYXRpbmcgZmFudGFzeSBwYWludGluZ3M/ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgV29uX2FydHMgICAgICAgICAgICAgICB8IFdvbiBhcnQgY29tcGV0aXRpb25zPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgVGltZV9hcnQgICAgICAgICAgICAgICB8IFRpbWUgdXRpbGl6ZWQgaW4gQXJ0cz8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgUHJlZGljdGVkIEhvYmJ5ICAgICAgICB8IHByZWRpY3Rpb25zIGZvciB0aGUgaG9iYnkgdGhhdCB0aGUga2lkIHdvdWxkbCBpa2UgICAgICAgICAgICAgfCBDYXRlZ29yaWNhbCAgIHwNCj09PT09PT0NCj4+Pj4+Pj4gU3Rhc2hlZCBjaGFuZ2VzDQoNCioqR2VuZXJhbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YSBzZXQ6KioNCg0KYGBge3J9DQpzdHIoSG9iYnlfRGF0YSkNCmBgYA0KDQotICAgKipzYW1wbGVzIG9mIHJhdyBkYXRhc2V0OioqDQoNCmBgYHtyfQ0Kc2FtcGxlKEhvYmJ5X0RhdGEpDQpgYGANCg0KLSAgICoqdmFyaWFibGVzIGRpc3RyaWJ1dGlvbjoqKg0KDQogICAgSW4gb3VyIGRhdGFzZXQsIG51bWVyaWMgdmFyaWFibGVzIGFyZSBub3QgYXZhaWxhYmxlOyBpbnN0ZWFkLCB3ZSBoYXZlIHRocmVlIG9yZGluYWwgdmFyaWFibGVzLiBEdWUgdG8gdGhlIG5hdHVyZSBvZiBvdXIgZGF0YSB0eXBlcywgY2VydGFpbiB0eXBlcyBvZiBncmFwaHMsIHN1Y2ggYXMgc2NhdHRlciBwbG90cyBhbmQgYm94IHBsb3RzLCB3ZXJlIG5vdCBzdWl0YWJsZSBmb3Igb3VyIGFuYWx5c2lzLg0KDQp2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIFRpbWVfc3BydDoNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJtYWdyaXR0ciIpICMgaW5zdGFsbCBvbmx5IG9uZSB0aW1lIHRoZW4gcHV0IHRoaXMgY29tbWFuZCBhcyBjb21tZW50IGFmdGVyIGluc3RhbGxhdGlvbg0KbGlicmFyeShtYWdyaXR0cikgIyMgZm9yIHBpcGUgb3BlcmF0aW9ucw0KSG9iYnlfRGF0YSRUaW1lX2FydCAlPiUgZGVuc2l0eSgpICU+JSBwbG90KG1haW49J3ZhcmlhYmxlcyBkaXN0cmlidXRpb24gb2YgVGltZV9hcnQnKQ0KDQpgYGANCg0KSW4gdGhlICJUaW1lX2FydCIgdmFyaWFibGUsIHBhcmVudHMgd2VyZSByZXF1ZXN0ZWQgdG8gYXNzZXNzIHRoZSB0aW1lIHRoZWlyIGNoaWxkIGRlZGljYXRlZCB0byBhcnRpc3RpYyBwdXJzdWl0cyBsaWtlIHBhaW50aW5nIG9yIHBhcGVyIGNyYWZ0aW5nLCB1c2luZyBhIHNjYWxlIHJhbmdpbmcgZnJvbSAxIHRvIDYsIHdoZXJlIDYgcmVwcmVzZW50cyB0aGUgaGlnaGVzdCBsZXZlbCBvZiBpbnZvbHZlbWVudC4gSXQncyB3b3J0aCBub3RpbmcgdGhhdCB0aGUgY29uY2VudHJhdGlvbiBvZiBsb3dlciByYXRpbmdzIGF0IHRoZSBsb3dlciBlbmQgb2YgdGhlIHNjYWxlICgxKSBpcyBxdWl0ZSBwcm9ub3VuY2VkLCBhbmQgdGhpcyB0ZW5kZW5jeSBtYXkgYmUgYXR0cmlidXRlZCB0byB0aGUgaW5oZXJlbnQgaW5jbGluYXRpb24gb2YgY2hpbGRyZW4gdG93YXJkcyBwaHlzaWNhbCBhY3Rpdml0aWVzLg0KDQp2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIFRpbWVfYXJ0Og0KDQpgYGB7cn0NCkhvYmJ5X0RhdGEkVGltZV9zcHJ0ICU+JSBkZW5zaXR5KCkgJT4lIHBsb3QobWFpbj0ndmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiBUaW1lX3NwcnQnKQ0KYGBgDQoNClBhcmVudHMgd2VyZSByZXF1ZXN0ZWQgdG8gYXNzZXNzIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gc3BvcnRzIG9uIGEgc2NhbGUgZnJvbSAxIHRvIDYgd2l0aGluIHRoZSAiVGltZV9zcHJ0IiB2YXJpYWJsZS4gTm90YWJseSwgdGhlIG1vc3QgcHJldmFsZW50IHJhbmtpbmcgd2FzIDMsIHN1Z2dlc3RpbmcgYSBtb2RlcmF0ZSBsZXZlbCBvZiBzcG9ydHMgcGFydGljaXBhdGlvbi4gSXQncyBpbnRlcmVzdGluZyB0byBvYnNlcnZlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBleGhpYml0cyBhIHNoYXBlIGFraW4gdG8gYSBiZWxsIGN1cnZlLCBpbmRpY2F0aW5nIHRoYXQgYSBzdWJzdGFudGlhbCBwcm9wb3J0aW9uIG9mIGNoaWxkcmVuIGhhdmUgYSBnZW51aW5lIGxvdmUgZm9yIHNwb3J0cy4NCg0KdmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiBHcmFzcF9wb3c6DQoNCmBgYHtyfQ0KSG9iYnlfRGF0YSRHcmFzcF9wb3cgJT4lIGRlbnNpdHkoKSAlPiUgcGxvdChtYWluPSd2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIEdyYXNwX3BvdycpDQpgYGANCg0KVGhlIGRlbnNpdHkgZ3JhcGggZGVwaWN0aW5nIHBhcmVudHMnIHJhdGluZ3Mgb2YgdGhlaXIgY2hpbGRyZW4ncyBncmFzcCBwb3dlciwgd2hpY2ggcmFuZ2VzIGZyb20gMSB0byA2LCBpbGx1c3RyYXRlcyBhIHRyZW5kIHdoZXJlIHRoZSBtb3N0IGNvbW1vbiByYXRpbmcgaXMgbGV2ZWwgMywgZm9sbG93ZWQgYnkgbGV2ZWwgNCwgbGV2ZWwgNSwgbGV2ZWwgMiwgbGV2ZWwgMSwgYW5kIGxldmVsIDYuIFRoaXMgZGlzdHJpYnV0aW9uIGluZGljYXRlcyB0aGF0IGEgc3Vic3RhbnRpYWwgcHJvcG9ydGlvbiBvZiBwYXJlbnRzIGJlbGlldmUgdGhlaXIgY2hpbGRyZW4gcG9zc2VzcyBncmFzcCBwb3dlciB0aGF0IGlzIGF2ZXJhZ2Ugb3Igc2xpZ2h0bHkgYWJvdmUgYXZlcmFnZSAobGV2ZWxzIDMgYW5kIDQpLCB3aXRoIGZld2VyIGNoaWxkcmVuIHJhdGVkIGF0IHRoZSBleHRyZW1lcyAobGV2ZWxzIDEsIDIsIDUsIGFuZCA2KS4NCg0KdmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiB0aGUgY2xhc3MgbGFiZWwgJ1ByZWRpY3RlZCBIb2JieSc6DQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKSAjIGluc3RhbGwgb25seSBvbmUgdGltZSB0aGVuIHB1dCB0aGlzIGNvbW1hbmQgYXMgY29tbWVudCBhZnRlciBpbnN0YWxsYXRpb24NCmxpYnJhcnkoZHBseXIpDQoNCmRhdGFzZXQyIDwtIEhvYmJ5X0RhdGEgJT4lIHNhbXBsZV9uKDE2MDApDQp0YWJsZShkYXRhc2V0MiRgUHJlZGljdGVkIEhvYmJ5YCkgJT4lIHBpZSgpDQp0YWIgPC0gZGF0YXNldDIkYFByZWRpY3RlZCBIb2JieWAgJT4lIHRhYmxlKCkNCnByZWNlbnRhZ2VzIDwtIHRhYiAlPiUgcHJvcC50YWJsZSgpICU+JSByb3VuZCgzKSAqIDEwMCANCnR4dCA8LSBwYXN0ZTAobmFtZXModGFiKSwgJ1xuJywgcHJlY2VudGFnZXMsICclJykNCnBpZSh0YWIsIGxhYmVscz10eHQpDQpgYGANCg0KVGhlIHBpZSBjaGFydCBpbGx1c3RyYXRlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBjbGFzcyBsYWJlbCAnUHJlZGljdGVkIEhvYmJ5Jy4gSXQncyBldmlkZW50IHRoYXQgYSBzdWJzdGFudGlhbCBwb3J0aW9uLCBhcHByb3hpbWF0ZWx5IDQzLjclLCBvZiB0aGUgY2hpbGRyZW4ncyBob2JiaWVzIGFyZSBhY2FkZW1pYyBpbiBuYXR1cmUsIGluZGljYXRpbmcgYSBzdHJvbmcgaW50ZXJlc3QgaW4gZWR1Y2F0aW9uYWwgcHVyc3VpdHMuIEFkZGl0aW9uYWxseSwgMzAuOCUgb2YgdGhlIGtpZHMgYXJlIGVuZ2FnZWQgaW4gc3BvcnRzLCByZWZsZWN0aW5nIGEgc2lnbmlmaWNhbnQgaW5jbGluYXRpb24gdG93YXJkcyBwaHlzaWNhbCBhY3Rpdml0aWVzLiBBcnRzLXJlbGF0ZWQgaG9iYmllcyBhY2NvdW50IGZvciAyNS42JSBvZiB0aGUgdG90YWwsIHRoZSBkaXN0cmlidXRpb24gcmVmbGVjdHMgYSBoYXJtb25pb3VzIGJsZW5kIG9mIGhvYmJpZXMuIFRoaXMgYmFsYW5jZWQgZGlzdHJpYnV0aW9uIG5vdCBvbmx5IHNpZ25pZmllcyBhIHZhcmlldHkgb2YgaW50ZXJlc3RzIGJ1dCBhbHNvIGluZGljYXRlcyBhIHdlbGwtcm91bmRlZCBlbmdhZ2VtZW50IG9mIGNoaWxkcmVuIGluIGFjYWRlbWljLCBwaHlzaWNhbCwgYW5kIGNyZWF0aXZlIGFjdGl2aXRpZXMuDQoNCg0KKipTdGF0aXN0aWNhbCBtZWFzdXJlcyA6KioNCg0KYGBge3J9DQpmaW5kX21vZGUgPC0gZnVuY3Rpb24oeCkgew0KICB1IDwtIHVuaXF1ZSh4KQ0KICB0YWIgPC0gdGFidWxhdGUobWF0Y2goeCwgdSkpDQogIHVbdGFiID09IG1heCh0YWIpXQ0KfQ0KDQpmaW5kX21vZGUoSG9iYnlfRGF0YSRUaW1lX2FydCkNCiMgVXBkYXRlZCB1cHN0cmVhbQ0KaGlzdChIb2JieV9EYXRhJFRpbWVfYXJ0KQ0KDQojU3Rhc2hlZCBjaGFuZ2VzDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBtYWtlcyBpdCBldmlkZW50IHRoYXQgdGhlIG1vZGUgaXMgZXF1YWwgdG8gMSwgYW5kIHdlIGJlbGlldmUgdGhhdCB0aGUgaGlnaCBmcmVxdWVuY3kgb2YgcGFyZW50cyByYW5raW5nICIxIiBhcyB0aGUgbW9zdCBjaG9zZW4gcmFuayBpbiB0aGUgIlRpbWVfYXJ0IiB2YXJpYWJsZSBjb3VsZCBiZSBhdHRyaWJ1dGVkIHRvIHNldmVyYWwgZmFjdG9ycy4gSXQgbWlnaHQgaW5kaWNhdGUgdGhhdCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBwYXJlbnRzIHBlcmNlaXZlIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gYXJ0IGFjdGl2aXRpZXMgYXMgcmVsYXRpdmVseSBsb3csIHBvc3NpYmx5IGR1ZSB0byB0aW1lIGNvbnN0cmFpbnRzLCBhY2FkZW1pYyBwcmlvcml0aWVzLCBvciBhIGxpbWl0ZWQgaW50ZXJlc3QgaW4gYXJ0LiBBbHRlcm5hdGl2ZWx5LCBpdCBjb3VsZCByZWZsZWN0IHRoYXQgcGFyZW50cyB2YWx1ZSBhIG1vcmUgYmFsYW5jZWQgYXBwcm9hY2ggdG8gdGhlaXIgY2hpbGRyZW4ncyBhY3Rpdml0aWVzLCB3aXRoIGEgdmFyaWV0eSBvZiBpbnRlcmVzdHMgYW5kIHJlc3BvbnNpYmlsaXRpZXMgc2hhcmluZyB0aGVpciB0aW1lLiBUaGlzIHRyZW5kIGNvdWxkIGFsc28gcmVzdWx0IGZyb20gYSBjdWx0dXJhbCBvciBlZHVjYXRpb25hbCBlbXBoYXNpcyBvbiBvdGhlciBzdWJqZWN0cyBhbmQgZXh0cmFjdXJyaWN1bGFyIGFjdGl2aXRpZXMgdGhhdCBjb21wZXRlIGZvciBhIGNoaWxkJ3MgdGltZSwgcG90ZW50aWFsbHkgbGVhZGluZyB0byBhIGxvd2VyIHJhbmtpbmcgZm9yIGFydC1yZWxhdGVkIGFjdGl2aXRpZXMuDQoNCmBgYHtyfQ0KZmluZF9tb2RlKEhvYmJ5X0RhdGEkR3Jhc3BfcG93KQ0KaGlzdChIb2JieV9EYXRhJEdyYXNwX3BvdykNCmBgYA0KDQpSYW5rIDMsIGluIHRoaXMgY29udGV4dCwgbWF5IGhhdmUgYmVlbiB0aGUgbW9zdCBjaG9zZW4gcmFuayBiZWNhdXNlIGl0IGxpa2VseSByZXByZXNlbnRzIGFuIGF2ZXJhZ2Ugb3IgbW9kZXJhdGUgbGV2ZWwgb2YgZ3Jhc3AgcG93ZXIuIFBhcmVudHMgbWF5IGhhdmUgYXNzZXNzZWQgdGhlaXIgY2hpbGRyZW4ncyBncmFzcCBwb3dlciBhcyBuZWl0aGVyIGV4Y2VwdGlvbmFsbHkgc3Ryb25nIChyYW5rIDUgb3IgNikgbm9yIHBhcnRpY3VsYXJseSB3ZWFrIChyYW5rIDEgb3IgMiksIHJlc3VsdGluZyBpbiB0aGUgcHJlZmVyZW5jZSBmb3IgdGhlIG1pZGRsZS1yYW5raW5nIG9wdGlvbi4gVGhpcyBjaG9pY2UgY291bGQgcmVmbGVjdCBhIHBlcmNlcHRpb24gdGhhdCB0aGVpciBjaGlsZHJlbidzIGdyYXNwIHBvd2VyIGZhbGxzIHdpdGhpbiBhIHR5cGljYWwgb3IgZXhwZWN0ZWQgcmFuZ2UsIG1ha2luZyBpdCB0aGUgbW9zdCBjb21tb24gcmF0aW5nLg0KDQpgYGB7cn0NCmZpbmRfbW9kZShIb2JieV9EYXRhJFRpbWVfc3BydCkNCmhpc3QoSG9iYnlfRGF0YSRUaW1lX3NwcnQpDQpgYGANCg0KQXMgc2hvd24gaW4gdGhlIGhpc3RvZ3JhbSB0aGUgcmFuayAzIG9uIGEgc2NhbGUgb2YgMSB0byA2IHdhcyBsaWtlbHkgdGhlIG1vc3QgY2hvc2VuIHJhbmsgZm9yIGFzc2Vzc2luZyBjaGlsZHJlbidzIGludm9sdmVtZW50IGluIHNwb3J0cyBiZWNhdXNlIGl0IHJlcHJlc2VudHMgYSBiYWxhbmNlZCBtaWRkbGUgZ3JvdW5kLiBQYXJlbnRzIG1heSBoYXZlIHBlcmNlaXZlZCByYW5rIDMgYXMgaW5kaWNhdGluZyB0aGF0IHRoZWlyIGNoaWxkcmVuIGFyZSBtb2RlcmF0ZWx5IGludm9sdmVkIGluIHNwb3J0cywgbm90IGV4Y2Vzc2l2ZWx5IGNvbW1pdHRlZCBvciBkaXNpbnRlcmVzdGVkLiBUaGlzIG1pZGRsZS1vZi10aGUtcm9hZCByYW5raW5nIHJlZmxlY3RzIGEgY29tbW9uIHBlcnNwZWN0aXZlIHRoYXQgbWFueSBwYXJlbnRzIG1heSBob2xkLCBjb25zaWRlcmluZyB0aGF0IGV4dHJlbWUgcmFua2luZ3MsIHN1Y2ggYXMgMSBvciA2LCBtaWdodCBzdWdnZXN0IGVpdGhlciBhIGxhY2sgb2YgaW52b2x2ZW1lbnQgb3IgYW4gZXhjZXNzaXZlIGZvY3VzIG9uIHNwb3J0cywgd2hpY2ggbWF5IG5vdCBhbGlnbiB3aXRoIHRoZWlyIHBlcmNlcHRpb24gb2YgdGhlaXIgY2hpbGQncyBvdmVyYWxsIHdlbGwtcm91bmRlZCBkZXZlbG9wbWVudC4NCg0KVGhlIGhpc3RvZ3JhbSBncmFwaCBmb3IgIlRpbWVfYXJ0IiB2YXJpYWJsZQ0KYGBge3J9DQpoaXN0KEhvYmJ5X0RhdGEkVGltZV9hcnQpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBtYWtlcyBpdCBldmlkZW50IHRoYXQgdGhlIG1vZGUgaXMgZXF1YWwgdG8gMSwgYW5kIHdlIGJlbGlldmUgdGhhdCB0aGUgaGlnaCBmcmVxdWVuY3kgb2YgcGFyZW50cyByYW5raW5nICIxIiBhcyB0aGUgbW9zdCBjaG9zZW4gcmFuayBpbiB0aGUgIlRpbWVfYXJ0IiB2YXJpYWJsZSBjb3VsZCBiZSBhdHRyaWJ1dGVkIHRvIHNldmVyYWwgZmFjdG9ycy4gSXQgbWlnaHQgaW5kaWNhdGUgdGhhdCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBwYXJlbnRzIHBlcmNlaXZlIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gYXJ0IGFjdGl2aXRpZXMgYXMgcmVsYXRpdmVseSBsb3csIHBvc3NpYmx5IGR1ZSB0byB0aW1lIGNvbnN0cmFpbnRzLCBhY2FkZW1pYyBwcmlvcml0aWVzLCBvciBhIGxpbWl0ZWQgaW50ZXJlc3QgaW4gYXJ0LiBBbHRlcm5hdGl2ZWx5LCBpdCBjb3VsZCByZWZsZWN0IHRoYXQgcGFyZW50cyB2YWx1ZSBhIG1vcmUgYmFsYW5jZWQgYXBwcm9hY2ggdG8gdGhlaXIgY2hpbGRyZW4ncyBhY3Rpdml0aWVzLCB3aXRoIGEgdmFyaWV0eSBvZiBpbnRlcmVzdHMgYW5kIHJlc3BvbnNpYmlsaXRpZXMgc2hhcmluZyB0aGVpciB0aW1lLiBUaGlzIHRyZW5kIGNvdWxkIGFsc28gcmVzdWx0IGZyb20gYSBjdWx0dXJhbCBvciBlZHVjYXRpb25hbCBlbXBoYXNpcyBvbiBvdGhlciBzdWJqZWN0cyBhbmQgZXh0cmFjdXJyaWN1bGFyIGFjdGl2aXRpZXMgdGhhdCBjb21wZXRlIGZvciBhIGNoaWxkJ3MgdGltZSwgcG90ZW50aWFsbHkgbGVhZGluZyB0byBhIGxvd2VyIHJhbmtpbmcgZm9yIGFydC1yZWxhdGVkIGFjdGl2aXRpZXMuDQoNCg0KYGBge3J9DQpmaW5kX21vZGUoSG9iYnlfRGF0YSRHcmFzcF9wb3cpDQpgYGANClRoZSBoaXN0b2dyYW0gZ3JhcGggZm9yICJHcmFzcF9wb3ciIHZhcmlhYmxlDQpgYGB7cn0NCmhpc3QoSG9iYnlfRGF0YSRHcmFzcF9wb3cpDQpgYGANClJhbmsgMywgaW4gdGhpcyBjb250ZXh0LCBtYXkgaGF2ZSBiZWVuIHRoZSBtb3N0IGNob3NlbiByYW5rIGJlY2F1c2UgaXQgbGlrZWx5IHJlcHJlc2VudHMgYW4gYXZlcmFnZSBvciBtb2RlcmF0ZSBsZXZlbCBvZiBncmFzcCBwb3dlci4gUGFyZW50cyBtYXkgaGF2ZSBhc3Nlc3NlZCB0aGVpciBjaGlsZHJlbidzIGdyYXNwIHBvd2VyIGFzIG5laXRoZXIgZXhjZXB0aW9uYWxseSBzdHJvbmcgKHJhbmsgNSBvciA2KSBub3IgcGFydGljdWxhcmx5IHdlYWsgKHJhbmsgMSBvciAyKSwgcmVzdWx0aW5nIGluIHRoZSBwcmVmZXJlbmNlIGZvciB0aGUgbWlkZGxlLXJhbmtpbmcgb3B0aW9uLiBUaGlzIGNob2ljZSBjb3VsZCByZWZsZWN0IGEgcGVyY2VwdGlvbiB0aGF0IHRoZWlyIGNoaWxkcmVuJ3MgZ3Jhc3AgcG93ZXIgZmFsbHMgd2l0aGluIGEgdHlwaWNhbCBvciBleHBlY3RlZCByYW5nZSwgbWFraW5nIGl0IHRoZSBtb3N0IGNvbW1vbiByYXRpbmcuDQoNCg0KYGBge3J9DQpmaW5kX21vZGUoSG9iYnlfRGF0YSRUaW1lX3NwcnQpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBncmFwaCBmb3IgIlRpbWVfc3BydCIgdmFyaWFibGUNCmBgYHtyfQ0KaGlzdChIb2JieV9EYXRhJFRpbWVfc3BydCkNCmBgYA0KQXMgc2hvd24gaW4gdGhlIGhpc3RvZ3JhbSB0aGUgcmFuayAzIG9uIGEgc2NhbGUgb2YgMSB0byA2IHdhcyBsaWtlbHkgdGhlIG1vc3QgY2hvc2VuIHJhbmsgZm9yIGFzc2Vzc2luZyBjaGlsZHJlbidzIGludm9sdmVtZW50IGluIHNwb3J0cyBiZWNhdXNlIGl0IHJlcHJlc2VudHMgYSBiYWxhbmNlZCBtaWRkbGUgZ3JvdW5kLiBQYXJlbnRzIG1heSBoYXZlIHBlcmNlaXZlZCByYW5rIDMgYXMgaW5kaWNhdGluZyB0aGF0IHRoZWlyIGNoaWxkcmVuIGFyZSBtb2RlcmF0ZWx5IGludm9sdmVkIGluIHNwb3J0cywgbm90IGV4Y2Vzc2l2ZWx5IGNvbW1pdHRlZCBvciBkaXNpbnRlcmVzdGVkLiBUaGlzIG1pZGRsZS1vZi10aGUtcm9hZCByYW5raW5nIHJlZmxlY3RzIGEgY29tbW9uIHBlcnNwZWN0aXZlIHRoYXQgbWFueSBwYXJlbnRzIG1heSBob2xkLCBjb25zaWRlcmluZyB0aGF0IGV4dHJlbWUgcmFua2luZ3MsIHN1Y2ggYXMgMSBvciA2LCBtaWdodCBzdWdnZXN0IGVpdGhlciBhIGxhY2sgb2YgaW52b2x2ZW1lbnQgb3IgYW4gZXhjZXNzaXZlIGZvY3VzIG9uIHNwb3J0cywgd2hpY2ggbWF5IG5vdCBhbGlnbiB3aXRoIHRoZWlyIHBlcmNlcHRpb24gb2YgdGhlaXIgY2hpbGQncyBvdmVyYWxsIHdlbGwtcm91bmRlZCBkZXZlbG9wbWVudC4NCg0KDQoqKkRhdGEgcHJlcHJvY2Vzc2luZyoqDQoNCg0KKiojMSMgRGF0YSBjbGVhbmluZzoqKg0KDQpEdXJpbmcgdGhlIGRhdGEgY2xlYW5pbmcgc3RhZ2UsIGZpbmRpbmcgYW5kIGZpeGluZyBmYXVsdHMsIGluY29uc2lzdGVuY2llcywgYW5kIGVycm9ycyBpbiBhIGRhdGFzZXQgaGVscHMgaXQgYmUgbW9yZSByZWxpYWJsZSBhbmQgb2YgaGlnaGVyIHF1YWxpdHkgZm9yIGFuYWx5c2lzIGFuZCBtb2RlbGluZy4gVGhlcmUgYXJlIG1ldGhvZHMgZm9yIGhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzLCBkZXRlY3Rpbmcgb3V0bGllcnMsIHJlc29sdmluZyBpbmNvbnNpc3RlbmNpZXMsIGFuZCBzdGFuZGFyZGl6aW5nIGZvcm1hdHMuDQoNCmltcG9ydCBEYXRhc2V0IkhvYmJ5X0RhdGEiDQoNCmBgYHtyfQ0KDQpWaWV3KEhvYmJ5X0RhdGEpDQoNCnN0cihIb2JieV9EYXRhKQ0KYGBgDQoNCmNoZWNrIG1pc3NpbmcgdmFsdWUgOg0KDQpgYGB7cn0NCmlzLm5hKEhvYmJ5X0RhdGEpDQpgYGANCg0KZmluZCB0aGUgdG90YWwgbnVsbCB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQ6DQoNCmBgYHtyfQ0Kc3VtKGlzLm5hKEhvYmJ5X0RhdGEpKQ0KYGBgDQoNCkRlc2NyaXB0aW9uOg0KDQpUaGlzIHN0YWdlIGludm9sdmVzIGNoZWNraW5nIGFuZCBkZWxldGluZyBmb3IgbnVsbCBhbmQgbWlzc2luZyB2YWx1ZXMgYmVjYXVzZSB0aGV5IG1pZ2h0IGhhdmUgYSBzaWduaWZpY2FudCBpbXBhY3Qgb24gdGhlIGRhdGEgYW5kIGNhdXNlIGVycm9ycyBhbmQgbmVnYXRpdmUgZWZmZWN0cyBpbiBzdWJzZXF1ZW50IHN0ZXBzLldlIHNpbXBseSBsb29rZWQgZm9yIG1pc3NpbmcgdmFsdWVzLCBhbmQgdGhlcmUgYXJlIGEgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIEFjY29yZGluZyB0byBvdXIgaW52ZXN0aWdhdGlvbiwgdGhlIGRhdGFzZXQgZG9lcyBub3QgY29udGFpbiBhbnkgb3V0bGllcnMgc2luY2UgaXQgZG9lc24ndCBoYXZlIGEgbnVtZXJpY2FsIGRhdGEgdHlwZS4gQWRkaXRpb25hbGx5LCB0aGVyZSBhcmUgbm8gaW5jb25zaXN0ZW50IHZhbHVlcyBvciBvdGhlciBlcnJvcnMuDQoNCg0KRGF0YXNldCBhZnRlciBjbGVhbmluZyBzdGVwOg0KYGBge3J9DQpzYW1wbGUoSG9iYnlfRGF0YSkNCmBgYA0KDQoqKiMyI0VuY29kaW5nOioqDQpUaGlzIHN0ZXAgaW5jbHVkZXMgYSBDb252ZXJ0aW5nIGNhdGVnb3JpY2FsIG9yIG5vbi1udW1lcmljIGRhdGEgaW50byBhIG51bWVyaWNhbCBmb3JtYXQsIHdoaWNoIGlzIG5lY2Vzc2FyeSBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIHN1YnNlcXVlbnQgc3RlcHMgaW4gcHJlcHJvY2Vzc2luZy4NCg0KYGBge3J9DQpIb2JieV9EYXRhJE9seW1waWFkX1BhcnRpY2lwYXRpb24gPSBmYWN0b3IoSG9iYnlfRGF0YSRPbHltcGlhZF9QYXJ0aWNpcGF0aW9uLGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRTY2hvbGFyc2hpcCA9IGZhY3RvcihIb2JieV9EYXRhJFNjaG9sYXJzaGlwICwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFNjaG9vbCA9IGZhY3RvcihIb2JieV9EYXRhJFNjaG9vbCwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFByb2plY3RzID0gZmFjdG9yKEhvYmJ5X0RhdGEkUHJvamVjdHMsIGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRNZWRhbHMgPSBmYWN0b3IoSG9iYnlfRGF0YSRNZWRhbHMsIGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRDYXJlZXJfc3BydCA9IGZhY3RvcihIb2JieV9EYXRhJENhcmVlcl9zcHJ0LCBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSwgbGFiZWxzID0gYygwLCAxKSkNCkhvYmJ5X0RhdGEkQWN0X3NwcnQgPSBmYWN0b3IoSG9iYnlfRGF0YSRBY3Rfc3BydCwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJEZhbnRfYXJ0cyA9IGZhY3RvcihIb2JieV9EYXRhJEZhbnRfYXJ0cywgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFdvbl9hcnRzID0gZmFjdG9yKEhvYmJ5X0RhdGEkV29uX2FydHMsIGxldmVscyA9IGMoIk5vIiwgIk1heWJlIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDIsIDEpKQ0KSG9iYnlfRGF0YSRGYXZfc3ViID0gZmFjdG9yKEhvYmJ5X0RhdGEkRmF2X3N1YiwgbGV2ZWxzID0gYygiU2NpZW5jZSIsICJNYXRoZW1hdGljcyIsICJIaXN0b3J5L0dlb2dyYXBoeSIsICJBbnkgbGFuZ3VhZ2UiKSwgbGFiZWxzID0gYygxLCAyLCAzLCA0KSkNCkhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAgPC0gZmFjdG9yKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIGxldmVscyA9IGMoIkFjYWRlbWljcyIsICJBcnRzIiwgIlNwb3J0cyIpLCBsYWJlbHMgPSBjKDEsIDIsIDMpKQ0KYGBgDQoNCkRhdGFzZXQgYWZ0ZXIgRW5jb2RpbmcgOg0KYGBge3J9DQpzYW1wbGUoSG9iYnlfRGF0YSkNCmBgYA0KDQoNCioqIzMjIE5vcm1hbGl6YXRpb24gYW5kIERpc2NldGl6YXRpb246KioNCg0KV2UgZG9uJ3QgbmVlZCB0byB1c2Ugbm9ybWFsaXphdGlvbiBhbmQgZGlzY2V0aXphdGlvbiBpbiBvdXIgZGF0YXNldC4gU2luY2Ugb3VyIGRhdGFzZXQgZG9lc24ndCBoYXZlIG51bWVyaWMgYXR0cmlidXRlcyBhbmQgbm9ybWFsaXphdGlvbiBpbnZvbHZlcyBtYXRoZW1hdGljYWwgb3BlcmF0aW9ucywgd2hpY2ggY2FuIHJlc3VsdCBpbiBtZWFuaW5nbGVzcyB2YWx1ZXMgYW5kIGVycm9ycywgQWxzbywgYXBwbHlpbmcgZGlzY3JldGl6YXRpb24gbGVhZHMgdG8gYSBsb3NzIG9mIGluZm9ybWF0aW9uIGFuZCBjcmVhdGVzIGludGVydmFscyBhbmQgcmVsYXRpb25zaGlwcyB0aGF0IGRvbid0IGV4aXN0IGJldHdlZW4gdmFsdWVzLg0KDQoqKiM0I0ZlYXR1cmUgU2VsZWN0aW9uOioqDQoNClRvIGltcHJvdmUgdGhlIGFjY3VyYWN5IG9mIG91ciBwcmVkaWN0aW9ucyBmb3IgdGhlIHRhcmdldCBjbGFzcyAiUHJlZGljdGVkIEhvYmJ5IiBhbmQgZGVjcmVhc2UgdGhlIHByb2Nlc3NpbmcgdGltZSBvZiBvdXIgY2xhc3NpZmllciwgd2Ugd2lsbCB1dGlsaXplIGZlYXR1cmUgc2VsZWN0aW9uIHRlY2huaXF1ZXMuIFRoZXNlIHRlY2huaXF1ZXMgZW5hYmxlIHVzIHRvIGVsaW1pbmF0ZSByZWR1bmRhbnQgb3IgaXJyZWxldmFudCBhdHRyaWJ1dGVzIGZyb20gdGhlIGRhdGFzZXQsIHJlc3VsdGluZyBpbiBhIG1vcmUgY29uY2lzZSBzdWJzZXQgb2YgZmVhdHVyZXMgdGhhdCBwcm92aWRlIHRoZSBtb3N0IHZhbHVhYmxlIGluZm9ybWF0aW9uIGZvciBvdXIgcHJlZGljdGlvbnMuDQoNClNwZWNpZmljYWxseSwgd2Ugd2lsbCB1c2UgdHdvIGZlYXR1cmUgc2VsZWN0aW9uIG1ldGhvZHM6IFJhbmsgRmVhdHVyZXMgYnkgSW1wb3J0YW5jZSBhbmQgRmVhdHVyZSBTZWxlY3Rpb24gVXNpbmcgUmVjdXJzaXZlIEZlYXR1cmUgRWxpbWluYXRpb24gKFJGRSkuDQoNCjEuKipSYW5rIEZlYXR1cmVzIGJ5IEltcG9ydGFuY2U6KioNCg0KVGhpcyBtZXRob2QgdXNlZCBpbiB0aGUgY29kZSBoZWxwcyB1cyBkZXRlcm1pbmUgd2hpY2ggZmVhdHVyZXMgYXJlIG1vc3QgaW1wb3J0YW50IGZvciBwcmVkaWN0aW5nIHRoZSAiUHJlZGljdGVkIEhvYmJ5IiBjbGFzcyBsYWJlbCBpbiB0aGUgZGF0YXNldC4gSXQgdXRpbGl6ZXMgdGhlIFJhbmRvbSBGb3Jlc3QgYWxnb3JpdGhtLCBrbm93biBmb3IgaXRzIGFjY3VyYXRlIHByZWRpY3Rpb24gY2FwYWJpbGl0aWVzLiBUaGlzIG1ldGhvZCBjYWxjdWxhdGVzIHRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggZmVhdHVyZSBieSBhc3Nlc3NpbmcgaXRzIGNvbnRyaWJ1dGlvbiB0byB0aGUgb3ZlcmFsbCBhY2N1cmFjeSBvZiB0aGUgcHJlZGljdGlvbnMuIEJ5IHJhbmtpbmcgdGhlIGZlYXR1cmVzIGJhc2VkIG9uIHRoZWlyIGltcG9ydGFuY2UsIHdlIGNhbiBpZGVudGlmeSB0aGUgb25lcyB0aGF0IGhhdmUgdGhlIGdyZWF0ZXN0IGluZmx1ZW5jZSBvbiBkZXRlcm1pbmluZyBob2JiaWVzLg0KDQpFbnN1cmUgdGhlIHJlc3VsdHMgYXJlIHJlcGVhdGFibGUgYnkgc2V0dGluZyBhIHNlZWQ6DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNykNCg0KYGBgDQoNCkxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXM6DQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KaW5zdGFsbC5wYWNrYWdlcygicmFuZG9tRm9yZXN0IikNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmBgYA0KDQpTZXBhcmF0ZSB0aGUgcHJlZGljdG9ycyBhbmQgdGhlIGNsYXNzIGxhYmVsOg0KDQpgYGB7cn0NCnByZWRpY3RvcnMgPC0gSG9iYnlfRGF0YVssIC0xNF0gICMgRXhjbHVkaW5nIHRoZSBjbGFzcyBsYWJlbCAoUHJlZGljdGVkIEhvYmJ5KQ0KY2xhc3NfbGFiZWwgPC0gSG9iYnlfRGF0YSRgUHJlZGljdGVkIEhvYmJ5YA0KYGBgDQoNClRyYWluIGEgUmFuZG9tIEZvcmVzdCBtb2RlbDoNCg0KYGBge3J9DQptb2RlbCA8LSByYW5kb21Gb3Jlc3QocHJlZGljdG9ycywgY2xhc3NfbGFiZWwsIGltcG9ydGFuY2UgPSBUUlVFKQ0KYGBgDQoNCkdldCB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZToNCg0KYGBge3J9DQppbXBvcnRhbmNlIDwtIGltcG9ydGFuY2UobW9kZWwpDQpgYGANCg0KUmFuayB0aGUgZmVhdHVyZXMgYnkgaW1wb3J0YW5jZToNCg0KYGBge3J9DQpyYW5rZWRfZmVhdHVyZXMgPC0gc29ydChpbXBvcnRhbmNlWywgIk1lYW5EZWNyZWFzZUdpbmkiXSwgZGVjcmVhc2luZyA9IFRSVUUpDQpgYGANCg0KUHJpbnQgdGhlIHJhbmtlZCBmZWF0dXJlczoNCg0KYGBge3J9DQpwcmludChyYW5rZWRfZmVhdHVyZXMpDQoNCmJhcnBsb3QocmFua2VkX2ZlYXR1cmVzLCBob3JpeiA9IFRSVUUsIGxhcyA9IDEsIG1haW4gPSAiS2lkcyBIb2JieSBWYXJpYWJsZSBJbXBvcnRhbmNlIFJhbmtpbmciKQ0KYGBgDQoNCjIuKipGZWF0dXJlIFNlbGVjdGlvbiBVc2luZyBSRkU6KioNCg0KVGhlIFJlY3Vyc2l2ZSBGZWF0dXJlIEVsaW1pbmF0aW9uIChSRkUpIG1ldGhvZCB3aXRoIFJhbmRvbSBGb3Jlc3QgaXMgYSB0ZWNobmlxdWUgdXNlZCB0byBzZWxlY3QgdGhlIG1vc3QgaW1wb3J0YW50IGZlYXR1cmVzIGZvciBhY2N1cmF0ZSBwcmVkaWN0aW9ucyAuIEl0IGl0ZXJhdGl2ZWx5IGVsaW1pbmF0ZXMgbGVzcyByZWxldmFudCBmZWF0dXJlcywgcmV0cmFpbmluZyB0aGUgbW9kZWwgYXQgZWFjaCBzdGVwIHRvIGV2YWx1YXRlIHBlcmZvcm1hbmNlLiBCeSBmb2N1c2luZyBvbiB0aGUgbW9zdCBpbmZvcm1hdGl2ZSBmZWF0dXJlcywgUkZFIGltcHJvdmVzIHJlZHVjZXMgY29tcGxleGl0eSwgYW5kIGVuaGFuY2VzIHByZWRpY3Rpb24gYWNjdXJhY3kuIEl0IGlzIHBhcnRpY3VsYXJseSBlZmZlY3RpdmUgd2l0aCBSYW5kb20gRm9yZXN0IGR1ZSB0byBpdHMgYWJpbGl0eSB0byBoYW5kbGUgY29tcGxleCByZWxhdGlvbnNoaXBzIGFuZCBoaWdoLWRpbWVuc2lvbmFsIGRhdGEuIFJGRSBoZWxwcyBpZGVudGlmeSB0aGUgbW9zdCBpbXBvcnRhbnQgYXR0cmlidXRlcyBhc3NvY2lhdGVkIHdpdGggdGhlIHRhcmdldCB2YXJpYWJsZSwgZW5hYmxpbmcgdGhlIGNyZWF0aW9uIG9mIG1vcmUgZWZmaWNpZW50IGFuZCBhY2N1cmF0ZSBtb2RlbHMuDQoNCkVuc3VyZSB0aGUgcmVzdWx0cyBhcmUgcmVwZWF0YWJsZSBieSBzZXR0aW5nIGEgc2VlZDoNCg0KYGBge3J9DQpzZXQuc2VlZCg3KQ0KYGBgDQoNCkxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXM6DQoNCmBgYHtyfQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpEZWZpbmUgdGhlIGNvbnRyb2wgcGFyYW1ldGVycyBmb3IgUkZFIHVzaW5nIHJhbmRvbSBmb3Jlc3Qgc2VsZWN0aW9uIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCmNvbnRyb2wgPC0gcmZlQ29udHJvbChmdW5jdGlvbnMgPSByZkZ1bmNzLCBtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkNCmBgYA0KDQpFeHRyYWN0IHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGZyb20gSG9iYnlfRGF0YToNCg0KYGBge3J9DQpwcmVkaWN0b3JzIDwtIEhvYmJ5X0RhdGFbLCAtbmNvbChIb2JieV9EYXRhKV0NCmBgYA0KDQpDb252ZXJ0IHRoZSBvdXRjb21lIHZhcmlhYmxlIHRvIGEgZmFjdG9yOg0KDQpgYGB7cn0NCm91dGNvbWUgPC0gYXMuZmFjdG9yKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KUnVuIHRoZSBSRkUgYWxnb3JpdGhtOg0KDQpgYGB7cn0NCnJlc3VsdHMgPC0gcmZlKHByZWRpY3RvcnMsIG91dGNvbWUsIHNpemVzID0gMTpuY29sKEhvYmJ5X0RhdGEpLCByZmVDb250cm9sID0gY29udHJvbCkNCmBgYA0KDQpTdW1tYXJpemUgdGhlIHJlc3VsdHM6DQoNCmBgYHtyfQ0KcHJpbnQocmVzdWx0cykNCmBgYA0KDQpMaXN0IHRoZSBjaG9zZW4gZmVhdHVyZXMgc2VsZWN0ZWQgYnkgUkZFOg0KDQpgYGB7cn0NCnByZWRpY3RvcnMocmVzdWx0cykNCmBgYA0KDQpQbG90IHRoZSByZXN1bHRzOg0KDQpgYGB7cn0NCnBsb3QocmVzdWx0cywgdHlwZSA9IGMoImciLCAibyIpKQ0KYGBgDQoNCjMuKipSZW1vdmluZyBJcnJlbGV2YW50IENvbHVtbnM6KioNCg0KQnkgY29uc2lkZXJpbmcgYm90aCBSZWN1cnNpdmUgRmVhdHVyZSBFbGltaW5hdGlvbiAoUkZFKSBhbmQgUmFuayBCeSBJbXBvcnRhbmNlLCB3ZSBjYW4gbWFrZSBpbmZvcm1lZCBkZWNpc2lvbnMgYWJvdXQgZmVhdHVyZSByZWxldmFuY2UgYW5kIGltcGFjdCBvbiB0aGUgbW9kZWwuIEluIHRoaXMgY2FzZSwgdGhlIGNvbHVtbnMgIlNjaG9vbCwiICJNZWRhbHMiIHNob3VsZCBiZSBkZWxldGVkIGFzIHRoZXkgaGF2ZSBsb3dlciBpbXBvcnRhbmNlIHNjb3JlcyBjb21wYXJlZCB0byB0aGUgc2VsZWN0ZWQgdmFyaWFibGVzLiBSZW1vdmluZyB0aGVzZSBjb2x1bW5zIHNpbXBsaWZpZXMgdGhlIG1vZGVsIGFuZCByZWR1Y2VzIGRpbWVuc2lvbmFsaXR5LCBlbGltaW5hdGluZyBwb3RlbnRpYWwgbm9pc2UgYW5kIGlycmVsZXZhbnQgaW5mb3JtYXRpb24gdGhhdCBjb3VsZCBoaW5kZXIgYWNjdXJhdGUgcHJlZGljdGlvbnMuDQoNClJlbW92ZSB0aGUgc3BlY2lmaWVkIGNvbHVtbnMgZnJvbSB0aGUgSG9iYnlfS2lkcyBkYXRhc2V0Og0KDQpgYGB7cn0NCkhvYmJ5X0RhdGEgPC0gSG9iYnlfRGF0YVssICEoY29sbmFtZXMoSG9iYnlfRGF0YSkgJWluJSBjKCJTY2hvb2wiLCAiTWVkYWxzIikpXQ0KYGBgDQoNCkRpc3BsYXkgdGhlIHVwZGF0ZWQgZGF0YXNldCBhZnRlciBkZWxldGluZyBjb2x1bW5zOg0KDQpgYGB7cn0NCnNhbXBsZShIb2JieV9EYXRhKQ0KYGBgDQp3ZSBjYW4gc2VlIHRoYXQgdGhlIHR3byBjb2x1bW5zIGFyZSBkZWxldGVkKCJTY2hvb2wiLCAiTWVkYWxzIikuDQoNCg0KDQoNCioqQmFsYW5jZWQgRGF0YSoqDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIGNsYXNzIGltYmFsYW5jZQ0KY2xhc3NfaW1iYWxhbmNlIDwtIG1heChwcm9wLnRhYmxlKHRhYmxlKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWApKSkgLSBtaW4ocHJvcC50YWJsZSh0YWJsZShIb2JieV9EYXRhJGBQcmVkaWN0ZWQgSG9iYnlgKSkpDQoNCiMgUHJpbnQgdGhlIHJlc3VsdA0KcHJpbnQoY2xhc3NfaW1iYWxhbmNlKQ0KYGBgDQppZiBjbGFzc19pbWJhbGFuY2UgaXMgY2xvc2UgdG8gMCwgaXQgc3VnZ2VzdHMgdGhhdCB0aGUgcHJvcG9ydGlvbnMgb2YgZGlmZmVyZW50IGNsYXNzZXMgYXJlIHJlbGF0aXZlbHkgc2ltaWxhciwgaW5kaWNhdGluZyBhIGJhbGFuY2VkIGRhdGFzZXQuIENvbnZlcnNlbHksIGlmIGNsYXNzX2ltYmFsYW5jZSBpcyBsYXJnZXIsIGl0IHN1Z2dlc3RzIGEgbW9yZSBzaWduaWZpY2FudCBpbWJhbGFuY2UgYmV0d2VlbiB0aGUgY2xhc3Nlcy4NClRoZSBjYWxjdWxhdGVkIGNsYXNzIGltYmFsYW5jZSB2YWx1ZSBvZiAwLjE4MDUxMjIgc3VnZ2VzdHMgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsYXNzZXMgaW4gdGhlICJQcmVkaWN0ZWRfSG9iYnkiIGNvbHVtbiBvZiB0aGUgZGF0YXNldCBpcyByZWxhdGl2ZWx5IGJhbGFuY2VkLiBUaGUgY2xhc3MgaW1iYWxhbmNlIGlzIGEgbWVhc3VyZSBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBwcm9wb3J0aW9ucyBvZiB0aGUgbW9zdCBwcmV2YWxlbnQgYW5kIGxlYXN0IHByZXZhbGVudCBjbGFzc2VzLiBJbiB0aGlzIGNhc2UsIHRoZSB2YWx1ZSBpcyBjbG9zZSB0byAwLCBpbmRpY2F0aW5nIHRoYXQgdGhlcmUgaXMgYSBtaW5pbWFsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcHJvcG9ydGlvbnMgb2YgZGlmZmVyZW50IGNsYXNzZXMuIEEgbG93ZXItY2xhc3MgaW1iYWxhbmNlIHZhbHVlIGlzIGdlbmVyYWxseSBkZXNpcmFibGUsIGFzIGl0IHNpZ25pZmllcyBhIG1vcmUgZXZlbiBkaXN0cmlidXRpb24gb2YgaW5zdGFuY2VzIGFjcm9zcyBjbGFzc2VzLCB3aGljaCBjYW4gYmUgYmVuZWZpY2lhbCBmb3IgdGhlIHRyYWluaW5nIGFuZCBwZXJmb3JtYW5jZSBvZiBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4NCg0KDQoqKkRhdGEgTWluaW5nIFRlY2huaXF1ZSoqDQoNCg0KKipFdmFsdWF0aW9uIEFuZCBDb21wYXJpc29uKioNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gQ2xhc3NpZmljYXRpb24tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KQWZ0ZXIgcHJlcHJvY2Vzc2luZywgd2Ugd2lsbCBwcm9jZWVkIHRvIHRoZSBjbGFzc2lmaWNhdGlvbiBzdGVwLiBJbiB0aGlzIHBoYXNlLCBhcyBwYXJ0IG9mIHN1cGVydmlzZWQgbGVhcm5pbmcsIHdlIHdpbGwgYXBwbHkgYSBjbGFzc2lmaWNhdGlvbiBhbGdvcml0aG0gdG8gYXNzaWduIGVhY2ggZGF0YSBwb2ludCBpbnRvIHByZWRlZmluZWQgY2F0ZWdvcmllcyBiYXNlZCBvbiBpdHMgYXR0cmlidXRlcy4gVGhpcyBpbnZvbHZlcyBzZWxlY3RpbmcgdGhlIG1vc3QgcmVsZXZhbnQgZmVhdHVyZXMgdGhhdCBoYXZlIGJlZW4gY2xlYW5lZCBhbmQgZm9ybWF0dGVkIGR1cmluZyBwcmVwcm9jZXNzaW5nLiBUaGUgc2VsZWN0ZWQgbW9kZWwgd2lsbCB0aGVuIGxlYXJuIGZyb20gdHJhaW5pbmcgZGF0YSwgZW5hYmxpbmcgaXQgdG8gcHJlZGljdCB0aGUgY2F0ZWdvcnkgb2YgbmV3LCB1bnNlZW4gZGF0YSBhY2N1cmF0ZWx5LiBUaGlzIHN0ZXAgaXMgZXNzZW50aWFsIGZvciBtYWtpbmcgaW5mb3JtZWQgZGVjaXNpb25zIG9yIHByZWRpY3Rpb25zIGJhc2VkIG9uIHRoZSBkYXRhLg0KDQpXZSBpbXBsZW1lbnQgYSBkZWNpc2lvbiB0cmVlIG9uIHRoZSBkYXRhc2V0LCB3aGljaCBoYXMgYmVlbiBwYXJ0aXRpb25lZCBpbnRvIFRyYWluaW5nIGFuZCBUZXN0IHNldHMgdXNpbmcgdGhlIHBlcmNlbnRhZ2Ugc3BsaXQgbWV0aG9kLiBUaGlzIG1ldGhvZCBlbnN1cmVzIHRoYXQgZWFjaCBzdWJzZXQgaXMgYSByYW5kb21pemVkLCByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2YgdGhlIGVudGlyZSBkYXRhc2V0LCB0aHVzIG1pbmltaXppbmcgYmlhcyBhbmQgZW5hYmxpbmcgY29uc2lzdGVudCBtb2RlbCBwZXJmb3JtYW5jZSBldmFsdWF0aW9uLiBXZSBjaG9vc2UgdGhyZWUgZGlmZmVyZW50IHNwbGl0IHNpemVzOiAoIjkwJSIsICIxMCUiKSwgKCI4MCUiLCAiMjAlIiksIGFuZCAoIjcwJSIsICIzMCUiKS4gVGhlc2UgdmFyeWluZyBzaXplcyBhcmUgc2VsZWN0ZWQgdG8gcHJvdmlkZSBpbnNpZ2h0IGludG8gdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2Ugd2l0aCBkaWZmZXJlbnQgYW1vdW50cyBvZiBkYXRhLCB3aGljaCBpcyB2aXRhbCBmb3IgZGV0ZWN0aW5nIHVuaXF1ZSBwYXR0ZXJucyBhbmQgY29uZmlybWluZyB0aGUgbW9kZWwncyBjb25zaXN0ZW5jeSBpbiB2YXJpb3VzIHNpdHVhdGlvbnMuDQoNCkluIHRoZSBmaW5hbCBzdGVwcywgd2UgdXRpbGl6ZSBkYXRhIHZpc3VhbGl6YXRpb24gdG9vbHMgdG8gY3JlYXRlIHZpc3VhbCByZXByZXNlbnRhdGlvbnMgb2Ygb3VyIGRlY2lzaW9uIHRyZWVzLiBBZGRpdGlvbmFsbHksIHdlIGNvbmR1Y3QgYW4gZXhoYXVzdGl2ZSBldmFsdWF0aW9uIG9mIHRoZSBtb2RlbCwgZW1wbG95aW5nIGEgIkNvbmZ1c2lvbiBNYXRyaXgiIHRvIGlsbHVzdHJhdGUgdGhlIG91dGNvbWVzIGNsZWFybHkuDQoNCg0KDQoNCioqSW5mb3JtYXRpb24gR2FpbioqDQoNCkluZm9ybWF0aW9uIEdhaW4gaXMgcGFydGljdWxhcmx5IHVzZWZ1bCB3aGVuIGRlYWxpbmcgd2l0aCBjYXRlZ29yaWNhbCB0YXJnZXQgdmFyaWFibGVzIHVzaW5nIEluZm9ybWF0aW9uIEdhaW4gZm9yIHRoZSBpbml0aWFsIHBhcnRpdGlvbmluZyBvZiBhIGRhdGFzZXQgd2hlbiBidWlsZGluZyBhIGRlY2lzaW9uIHRyZWUgSXQncyBiYXNlZCBvbiB0aGUgY29uY2VwdCBvZiBlbnRyb3B5IGFuZCBhaW1zIHRvIG1heGltaXplIHRoZSBob21vZ2VuZWl0eSBvZiBzdWJzZXRzIGFmdGVyIGVhY2ggc3BsaXQuIFRoaXMgYXBwcm9hY2ggaGVscHMgY3JlYXRlIGFuIGVmZmVjdGl2ZSBkZWNpc2lvbiB0cmVlIGJ5IHNlbGVjdGluZyBmZWF0dXJlcyB0aGF0IHByb3ZpZGUgdGhlIG1vc3QgaW5mb3JtYXRpb24gZm9yIHByZWRpY3RpbmcgdGhlIHRhcmdldCB2YXJpYWJsZS4NCg0KDQoqKjEtSW5mb3JtYXRpb24gR2Fpbig3MCUsMzAlKSoqDQoNCmBgYHtyfQ0KIyBMb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcw0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FyZXQpDQoNCiMgU2V0IHRoZSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNldC5zZWVkKDEyMzQpDQoNCiMgU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzDQppbmQgPC0gc2FtcGxlKDIsIG5yb3coSG9iYnlfRGF0YSksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuNywgMC4zKSkNCnRyYWluRGF0YSA8LSBIb2JieV9EYXRhW2luZCA9PSAxLF0NCnRlc3REYXRhIDwtIEhvYmJ5X0RhdGFbaW5kID09IDIsXQ0KDQojIERlZmluZSB0aGUgZm9ybXVsYSBmb3IgdGhlIGRlY2lzaW9uIHRyZWUNCm15Rm9ybXVsYSA8LSBgUHJlZGljdGVkIEhvYmJ5YCB+IFNjaG9sYXJzaGlwICsgRmF2X3N1YiArIFByb2plY3RzICsgR3Jhc3BfcG93ICsgVGltZV9zcHJ0ICsgQ2FyZWVyX3NwcnQgKyBBY3Rfc3BydCArIEZhbnRfYXJ0cyArIFdvbl9hcnRzICsgVGltZV9hcnQgKyBPbHltcGlhZF9QYXJ0aWNpcGF0aW9uDQoNCiMgQ3JlYXRlIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsIHdpdGggdGhlICJpbmZvcm1hdGlvbiIgc3BsaXR0aW5nIGNyaXRlcmlvbg0KSG9iYnlfRGF0YV9jdHJlZSA8LSBycGFydChteUZvcm11bGEsIGRhdGEgPSB0cmFpbkRhdGEsIG1ldGhvZCA9ICJjbGFzcyIsIHBhcm1zID0gbGlzdChzcGxpdCA9ICJpbmZvcm1hdGlvbiIpKQ0KDQoNCg0KDQoNCiMgUHJpbnQgdGhlIGRlY2lzaW9uIHRyZWUNCnByaW50KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCiMgUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpycGFydC5wbG90KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCg0KDQoNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgSW5mb3JtYXRpb24gZ2Fpbig3MC8zMCk6KioNCg0KSW4gRnJpc3QgVHJlZSAsd2UgZGV2aWRlIGRhdGFzZXQgaW50byB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHdpdGggc2l6ZSglNzAsJTMwKSByZXNwZWN0aXZlbHkuIEFzIHlvdSBjYW4gc2VlIGluIHRoZSBmaWd1cmUsIHRoZSByb290IG5vZGUgKCJDYXJlZXJfc3BydCIpIHNlcnZlcyBhcyB0aGUgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiBwcm9jZXNzIHNpbmNlIGhhdmUgdGhlIGhlaWdodHMgR2Fpbi4gVGhlIGRhdGFzZXQgaGFzIGEgZGlzdHJpYnV0aW9uIG9mIGFwcHJveGltYXRlbHkgNDIuNDclIGZvciBjbGFzcyAxKCJBY2FkZW1pY3MiKSwgMjUuOTIlIGZvciBjbGFzcyAyKCJBcnRzIiksIGFuZCAzMS42MSUgZm9yIGNsYXNzIDMgKCJTcG9ydHMiKS4NCg0KVGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSAiQ2FyZWVyX3NwcnQiIGlmIGVxdWFsIDAsIHRyZWUgYnJhbmNoZXMgYmFzZWQgb24iV29uX2FydHMiICxhbmQgbWFqb3JpdHkgb2YgaW5zdGFuY2VzIGZhbGwgaW50byAiQWNhZGltY3MiLCBjb25zdGl0dXRpbmcgNjMuOTMlICwgaWYiV29uX2FydHMiIGVxdWFsIDAgb3IyIEluIHRoaXMgY2FzZSB0aGUgdHJlZSB0ZXJtaW5hdGVzIHdpdGggYSBsZWFmIG5vZGUgaW5kaWNhdGluZyBhIGhpZ2ggcHJvYmFiaWxpdHkgODkuMDglIGZvciAiQWNhZGltY3MiLmVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBiYXNlZCBvbiAiRmFudF9hcnRzIiBpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgODglLGlmIGVxdWFsIDAgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBY2FkZW1pY3MiIHdpdGggYSBwcm9iYWJpbGl0eSBvZiA3MyUgLGVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBcnRzIiB3aXRoIGEgaGlnaCBwcm9iYWJpbGl0eSA5NiUsIGlmICJDYXJlZXJfc3BydCIgaXMgMSwgdGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgIkZhbnRfYXJ0cyIgaW50byAiU3BvcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgODAlLiBJZiAiRmFudF9hcnRzIiBpcyAxICwgdGhlcmUgaXMgYW5vdGhlciBzcGxpdCBiYXNlZCBvbiB0aGUgIlRpbWVfYXJ0IiBmZWF0dXJlIGludG8gIkFydHMiIHdpdGggYSBwcm9iYWJpbGl0eSA0OSUuIElmICJUaW1lX2FydCIgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDMsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDg3JSAuIE9uIHRoZSBvdGhlciBoYW5kLCBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiAzLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDY1JSAuSWYgIkZhbnRfYXJ0cyIgbm90IGVxdWFsIDEgLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTUlLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIGxlYWYgbm9kZS4NCg0KKipGaXJzdCBDb25mdXNpb24gbWF0cml4KioNCg0KYGBge3J9DQojIFByZWRpY3Qgb24gdGhlIHRlc3QgZGF0YQ0KdGVzdFByZWQgPC0gcHJlZGljdChIb2JieV9EYXRhX2N0cmVlLCBuZXdkYXRhID0gdGVzdERhdGEsIHR5cGUgPSAnY2xhc3MnKQ0KDQojIENoZWNrIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwNCmFjY3VyYWN5IDwtIHN1bSh0ZXN0UHJlZCA9PSB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCkgLyBucm93KHRlc3REYXRhKSAqIDEwMA0KY2F0KCdBY2N1cmFjeTonLCBhY2N1cmFjeSwgJ1xuJykNCg0KIyBDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4DQpjb25mX21hdHJpeCA8LSB0YWJsZShBY3R1YWwgPSB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCwgUHJlZGljdGVkID0gdGVzdFByZWQpDQoNCiMgQ2FsY3VsYXRlIHByZWNpc2lvbiBmb3IgZWFjaCBjbGFzcw0KcHJlY2lzaW9uX2NsYXNzXzEgPC0gY29uZl9tYXRyaXhbMSwgMV0gLyBzdW0oY29uZl9tYXRyaXhbMSwgXSkNCnByZWNpc2lvbl9jbGFzc18yIDwtIGNvbmZfbWF0cml4WzIsIDJdIC8gc3VtKGNvbmZfbWF0cml4WzIsIF0pDQpwcmVjaXNpb25fY2xhc3NfMyA8LSBjb25mX21hdHJpeFszLCAzXSAvIHN1bShjb25mX21hdHJpeFszLCBdKQ0KDQojIENhbGN1bGF0ZSBzZW5zaXRpdml0eSBmb3IgZWFjaCBjbGFzcw0Kc2Vuc2l0aXZpdHlfY2xhc3NfMSA8LSBjb25mX21hdHJpeFsxLCAxXSAvIHN1bShjb25mX21hdHJpeFsxLCBdKQ0Kc2Vuc2l0aXZpdHlfY2xhc3NfMiA8LSBjb25mX21hdHJpeFsyLCAyXSAvIHN1bShjb25mX21hdHJpeFsyLCBdKQ0Kc2Vuc2l0aXZpdHlfY2xhc3NfMyA8LSBjb25mX21hdHJpeFszLCAzXSAvIHN1bShjb25mX21hdHJpeFszLCBdKQ0KDQojIENhbGN1bGF0ZSBzcGVjaWZpY2l0eSBmb3IgZWFjaCBjbGFzcw0Kc3BlY2lmaWNpdHlfY2xhc3NfMSA8LSBzdW0oZGlhZyhjb25mX21hdHJpeFstMSwgLTFdKSkgLyBzdW0oY29uZl9tYXRyaXhbLTEsIF0pDQpzcGVjaWZpY2l0eV9jbGFzc18yIDwtIHN1bShjb25mX21hdHJpeFtjKDEsIDMpLCBjKDEsIDMpXSkgLyBzdW0oY29uZl9tYXRyaXhbYygxLCAzKSwgXSkNCnNwZWNpZmljaXR5X2NsYXNzXzMgPC0gc3VtKGNvbmZfbWF0cml4W2MoMSwgMiksIGMoMSwgMildKSAvIHN1bShjb25mX21hdHJpeFtjKDEsIDIpLCBdKQ0KDQojIENhbGN1bGF0ZSBtYWNyby1hdmVyYWdlIHNlbnNpdGl2aXR5DQptYWNyb19hdmdfc2Vuc2l0aXZpdHkgPC0gKHNlbnNpdGl2aXR5X2NsYXNzXzEgKyBzZW5zaXRpdml0eV9jbGFzc18yICsgc2Vuc2l0aXZpdHlfY2xhc3NfMykgLyAzDQoNCiMgQ2FsY3VsYXRlIG1hY3JvLWF2ZXJhZ2Ugc3BlY2lmaWNpdHkNCm1hY3JvX2F2Z19zcGVjaWZpY2l0eSA8LSAoc3BlY2lmaWNpdHlfY2xhc3NfMSArIHNwZWNpZmljaXR5X2NsYXNzXzIgKyBzcGVjaWZpY2l0eV9jbGFzc18zKSAvIDMNCg0KIyBDYWxjdWxhdGUgbWFjcm8tYXZlcmFnZSBwcmVjaXNpb24NCm1hY3JvX2F2Z19wcmVjaXNpb24gPC0gKHByZWNpc2lvbl9jbGFzc18xICsgcHJlY2lzaW9uX2NsYXNzXzIgKyBwcmVjaXNpb25fY2xhc3NfMykgLyAzDQoNCiMgUHJpbnQgbWFjcm8tYXZlcmFnZSBzZW5zaXRpdml0eQ0KY2F0KCdBdmVyYWdlIFNlbnNpdGl2aXR5OicsIG1hY3JvX2F2Z19zZW5zaXRpdml0eSwgJ1xuJykNCg0KIyBQcmludCBtYWNyby1hdmVyYWdlIHNwZWNpZmljaXR5DQpjYXQoJ0F2ZXJhZ2UgU3BlY2lmaWNpdHk6JywgbWFjcm9fYXZnX3NwZWNpZmljaXR5LCAnXG4nKQ0KDQojIFByaW50IG1hY3JvLWF2ZXJhZ2UgcHJlY2lzaW9uDQpjYXQoJ0F2ZXJhZ2UgUHJlY2lzaW9uOicsIG1hY3JvX2F2Z19wcmVjaXNpb24sICdcbicpDQoNCg0KDQoNCg0KDQojIFByaW50IHByZWNpc2lvbiBmb3IgZWFjaCBjbGFzcw0KY2F0KCdQcmVjaXNpb24gZm9yIENsYXNzIDE6JywgcHJlY2lzaW9uX2NsYXNzXzEsICcgXG4nKQ0KDQpjYXQoJ1ByZWNpc2lvbiBmb3IgQ2xhc3MgMjonLCBwcmVjaXNpb25fY2xhc3NfMiwgJyAgXG4nKQ0KY2F0KCdQcmVjaXNpb24gZm9yIENsYXNzIDM6JywgcHJlY2lzaW9uX2NsYXNzXzMsICcgXG4nKQ0KDQojIFByaW50IHNlbnNpdGl2aXR5IGZvciBlYWNoIGNsYXNzDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAxOicsIHNlbnNpdGl2aXR5X2NsYXNzXzEsICdcbicpDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAyOicsIHNlbnNpdGl2aXR5X2NsYXNzXzIsICdcbicpDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAzOicsIHNlbnNpdGl2aXR5X2NsYXNzXzMsICdcbicpDQoNCiMgUHJpbnQgc3BlY2lmaWNpdHkgZm9yIGVhY2ggY2xhc3MNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDE6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMSwgJ1xuJykNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDI6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMiwgJ1xuJykNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDM6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMywgJ1xuJykNCg0KDQoNCg0KYGBgDQoNCg0KDQoqKjItSW5mb3JtYXRpb24gR2Fpbig4MCUsMjAlKSoqDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNCg0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjYXJldCkNCg0KIyBTZXQgdGhlIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0Kc2V0LnNlZWQoMTIzNCkNCg0KIyBTcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMNCmluZCA8LSBzYW1wbGUoMiwgbnJvdyhIb2JieV9EYXRhKSwgcmVwbGFjZT1UUlVFLCBwcm9iPWMoMC44LCAwLjIpKQ0KdHJhaW5EYXRhIDwtIEhvYmJ5X0RhdGFbaW5kID09IDEsXQ0KdGVzdERhdGEgPC0gSG9iYnlfRGF0YVtpbmQgPT0gMixdDQoNCiMgRGVmaW5lIHRoZSBmb3JtdWxhIGZvciB0aGUgZGVjaXNpb24gdHJlZQ0KbXlGb3JtdWxhIDwtIGBQcmVkaWN0ZWQgSG9iYnlgIH4gU2Nob2xhcnNoaXAgKyBGYXZfc3ViICsgUHJvamVjdHMgKyBHcmFzcF9wb3cgKyBUaW1lX3NwcnQgKyBDYXJlZXJfc3BydCArIEFjdF9zcHJ0ICsgRmFudF9hcnRzICsgV29uX2FydHMgKyBUaW1lX2FydCArIE9seW1waWFkX1BhcnRpY2lwYXRpb24NCg0KIyBDcmVhdGUgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgd2l0aCB0aGUgImluZm9ybWF0aW9uIiBzcGxpdHRpbmcgY3JpdGVyaW9uDQpIb2JieV9EYXRhX2N0cmVlIDwtIHJwYXJ0KG15Rm9ybXVsYSwgZGF0YSA9IHRyYWluRGF0YSwgbWV0aG9kID0gImNsYXNzIiwgcGFybXMgPSBsaXN0KHNwbGl0ID0gImluZm9ybWF0aW9uIikpDQoNCg0KDQoNCg0KIyBQcmludCB0aGUgZGVjaXNpb24gdHJlZQ0KcHJpbnQoSG9iYnlfRGF0YV9jdHJlZSkNCg0KIyBQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCnJwYXJ0LnBsb3QoSG9iYnlfRGF0YV9jdHJlZSkNCg0KDQoNCg0KDQoNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgSW5mb3JtYXRpb24gZ2Fpbig4MC8yMCk6KioNCg0KSW4gU2Vjb25kIFRyZWUgLHdlIGRldmlkZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgc2V0IGFuZCB0ZXN0IHNldCB3aXRoIHNpemUoJTgwLCUyMCkgcmVzcGVjdGl2ZWx5LiBBcyB5b3UgY2FuIHNlZSBpbiB0aGUgZmlndXJlLCB0aGUgcm9vdCBub2RlICgiQ2FyZWVyX3NwcnQiKSBzZXJ2ZXMgYXMgdGhlIHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgY2xhc3NpZmljYXRpb24gcHJvY2VzcyBzaW5jZSBoYXZlIHRoZSBoZWlnaHRzIEdhaW4uIFRoZSBkYXRhc2V0IGhhcyBhIGRpc3RyaWJ1dGlvbiBvZiBhcHByb3hpbWF0ZWx5IDQzJWZvciBjbGFzczEgKCJBY2FkZW1pY3MiKSwgMjYlIGZvciBjbGFzcyAyKCJBcnRzIiksIGFuZCAzMSUgZm9yIGNsYXNzIDMgKCJTcG9ydHMiKS4NCg0KVGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSAiQ2FyZWVyX3NwcnQiIGlmIGVxdWFsIDAsIHRyZWUgYnJhbmNoZXMgYmFzZWQgb24iV29uX2FydHMiICxhbmQgbWFqb3JpdHkgb2YgaW5zdGFuY2VzIGZhbGwgaW50byAiQWNhZGltY3MiLCBjb25zdGl0dXRpbmcgNjUlICwgaWYiV29uX2FydHMiIGVxdWFsIDAgb3IyIEluIHRoaXMgY2FzZSB0aGUgdHJlZSB0ZXJtaW5hdGVzIHdpdGggYSBsZWFmIG5vZGUgaW5kaWNhdGluZyBhIGhpZ2ggcHJvYmFiaWxpdHkgOTAlIGZvciAiQWNhZGltY3MiLmVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBiYXNlZCBvbiAiRmFudF9hcnRzImludG8gIkFydHMiIHdpdGggYSBwcm9iYWJpbGl0eSA4OCUsaWYgZXF1YWwgMCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIkFjYWRlbWljcyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDcyJSAsZWxzZSB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIkFydHMiIHdpdGggYSBoaWdoIHByb2JhYmlsaXR5IDk2JSwgaWYgIkNhcmVlcl9zcHJ0IiBpcyAxLCB0aGUgdHJlZSBmdXJ0aGVyIGJyYW5jaGVzIGJhc2VkIG9uIHRoZSAiRmFudF9hcnRzIiBpbnRvICJTcG9ydHMiIHdpdGggYSBwcm9iYWJpbGl0eSA3OCUuIElmICJGYW50X2FydHMiIGlzIDEgLCB0aGVyZSBpcyBhbm90aGVyIHNwbGl0IGJhc2VkIG9uIHRoZSAiVGltZV9hcnQiIGludG8gIkFydHMiIHdpdGggYSBwcm9iYWJpbGl0eSA1MCUuIElmICJUaW1lX2FydCIgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDMsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDg1JSAuIE9uIHRoZSBvdGhlciBoYW5kLCBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiAzLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDU4JSAuSWYgIkZhbnRfYXJ0cyIgSWYgbm90IGVxdWFsIDEgLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTQlLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIGxlYWYgbm9kZS4NCg0KKipTZWNvbmQgQ29uZnVzaW9uIG1hdHJpeCoqDQoNCmBgYHtyfQ0KDQojIFByZWRpY3Qgb24gdGhlIHRlc3QgZGF0YQ0KdGVzdFByZWQgPC0gcHJlZGljdChIb2JieV9EYXRhX2N0cmVlLCBuZXdkYXRhID0gdGVzdERhdGEsIHR5cGUgPSAnY2xhc3MnKQ0KDQojIENoZWNrIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwNCmFjY3VyYWN5IDwtIHN1bSh0ZXN0UHJlZCA9PSB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCkgLyBucm93KHRlc3REYXRhKSAqIDEwMA0KY2F0KCdBY2N1cmFjeTonLCBhY2N1cmFjeSwgJ1xuJykNCg0KIyBDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4DQpjb25mX21hdHJpeCA8LSB0YWJsZShBY3R1YWwgPSB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCwgUHJlZGljdGVkID0gdGVzdFByZWQpDQoNCiMgQ2FsY3VsYXRlIHByZWNpc2lvbiBmb3IgZWFjaCBjbGFzcw0KcHJlY2lzaW9uX2NsYXNzXzEgPC0gY29uZl9tYXRyaXhbMSwgMV0gLyBzdW0oY29uZl9tYXRyaXhbMSwgXSkNCnByZWNpc2lvbl9jbGFzc18yIDwtIGNvbmZfbWF0cml4WzIsIDJdIC8gc3VtKGNvbmZfbWF0cml4WzIsIF0pDQpwcmVjaXNpb25fY2xhc3NfMyA8LSBjb25mX21hdHJpeFszLCAzXSAvIHN1bShjb25mX21hdHJpeFszLCBdKQ0KDQojIENhbGN1bGF0ZSBzZW5zaXRpdml0eSBmb3IgZWFjaCBjbGFzcw0Kc2Vuc2l0aXZpdHlfY2xhc3NfMSA8LSBjb25mX21hdHJpeFsxLCAxXSAvIHN1bShjb25mX21hdHJpeFsxLCBdKQ0Kc2Vuc2l0aXZpdHlfY2xhc3NfMiA8LSBjb25mX21hdHJpeFsyLCAyXSAvIHN1bShjb25mX21hdHJpeFsyLCBdKQ0Kc2Vuc2l0aXZpdHlfY2xhc3NfMyA8LSBjb25mX21hdHJpeFszLCAzXSAvIHN1bShjb25mX21hdHJpeFszLCBdKQ0KDQojIENhbGN1bGF0ZSBzcGVjaWZpY2l0eSBmb3IgZWFjaCBjbGFzcw0Kc3BlY2lmaWNpdHlfY2xhc3NfMSA8LSBzdW0oZGlhZyhjb25mX21hdHJpeFstMSwgLTFdKSkgLyBzdW0oY29uZl9tYXRyaXhbLTEsIF0pDQpzcGVjaWZpY2l0eV9jbGFzc18yIDwtIHN1bShjb25mX21hdHJpeFtjKDEsIDMpLCBjKDEsIDMpXSkgLyBzdW0oY29uZl9tYXRyaXhbYygxLCAzKSwgXSkNCnNwZWNpZmljaXR5X2NsYXNzXzMgPC0gc3VtKGNvbmZfbWF0cml4W2MoMSwgMiksIGMoMSwgMildKSAvIHN1bShjb25mX21hdHJpeFtjKDEsIDIpLCBdKQ0KDQojIENhbGN1bGF0ZSBtYWNyby1hdmVyYWdlIHNlbnNpdGl2aXR5DQptYWNyb19hdmdfc2Vuc2l0aXZpdHkgPC0gKHNlbnNpdGl2aXR5X2NsYXNzXzEgKyBzZW5zaXRpdml0eV9jbGFzc18yICsgc2Vuc2l0aXZpdHlfY2xhc3NfMykgLyAzDQoNCiMgQ2FsY3VsYXRlIG1hY3JvLWF2ZXJhZ2Ugc3BlY2lmaWNpdHkNCm1hY3JvX2F2Z19zcGVjaWZpY2l0eSA8LSAoc3BlY2lmaWNpdHlfY2xhc3NfMSArIHNwZWNpZmljaXR5X2NsYXNzXzIgKyBzcGVjaWZpY2l0eV9jbGFzc18zKSAvIDMNCg0KIyBDYWxjdWxhdGUgbWFjcm8tYXZlcmFnZSBwcmVjaXNpb24NCm1hY3JvX2F2Z19wcmVjaXNpb24gPC0gKHByZWNpc2lvbl9jbGFzc18xICsgcHJlY2lzaW9uX2NsYXNzXzIgKyBwcmVjaXNpb25fY2xhc3NfMykgLyAzDQoNCiMgUHJpbnQgbWFjcm8tYXZlcmFnZSBzZW5zaXRpdml0eQ0KY2F0KCdBdmVyYWdlIFNlbnNpdGl2aXR5OicsIG1hY3JvX2F2Z19zZW5zaXRpdml0eSwgJ1xuJykNCg0KIyBQcmludCBtYWNyby1hdmVyYWdlIHNwZWNpZmljaXR5DQpjYXQoJ0F2ZXJhZ2UgU3BlY2lmaWNpdHk6JywgbWFjcm9fYXZnX3NwZWNpZmljaXR5LCAnXG4nKQ0KDQojIFByaW50IG1hY3JvLWF2ZXJhZ2UgcHJlY2lzaW9uDQpjYXQoJ0F2ZXJhZ2UgUHJlY2lzaW9uOicsIG1hY3JvX2F2Z19wcmVjaXNpb24sICdcbicpDQoNCg0KDQoNCg0KDQojIFByaW50IHByZWNpc2lvbiBmb3IgZWFjaCBjbGFzcw0KY2F0KCdQcmVjaXNpb24gZm9yIENsYXNzIDE6JywgcHJlY2lzaW9uX2NsYXNzXzEsICcgXG4nKQ0KDQpjYXQoJ1ByZWNpc2lvbiBmb3IgQ2xhc3MgMjonLCBwcmVjaXNpb25fY2xhc3NfMiwgJyAgXG4nKQ0KY2F0KCdQcmVjaXNpb24gZm9yIENsYXNzIDM6JywgcHJlY2lzaW9uX2NsYXNzXzMsICcgXG4nKQ0KDQojIFByaW50IHNlbnNpdGl2aXR5IGZvciBlYWNoIGNsYXNzDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAxOicsIHNlbnNpdGl2aXR5X2NsYXNzXzEsICdcbicpDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAyOicsIHNlbnNpdGl2aXR5X2NsYXNzXzIsICdcbicpDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAzOicsIHNlbnNpdGl2aXR5X2NsYXNzXzMsICdcbicpDQoNCiMgUHJpbnQgc3BlY2lmaWNpdHkgZm9yIGVhY2ggY2xhc3MNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDE6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMSwgJ1xuJykNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDI6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMiwgJ1xuJykNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDM6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMywgJ1xuJykNCg0KDQoNCg0KDQoNCg0KYGBgDQoNCioqMy1JbmZvcm1hdGlvbiBHYWluKDkwJSwxMCUpKioNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KDQpsaWJyYXJ5KGNhVG9vbHMpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpsaWJyYXJ5KGNhcmV0KQ0KIyBTZXQgdGhlIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0Kc2V0LnNlZWQoMTIzNCkNCg0KIyBTcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMNCmluZCA8LSBzYW1wbGUoMiwgbnJvdyhIb2JieV9EYXRhKSwgcmVwbGFjZT1UUlVFLCBwcm9iPWMoMC45LCAwLjEpKQ0KdHJhaW5EYXRhIDwtIEhvYmJ5X0RhdGFbaW5kID09IDEsXQ0KdGVzdERhdGEgPC0gSG9iYnlfRGF0YVtpbmQgPT0gMixdDQoNCiMgRGVmaW5lIHRoZSBmb3JtdWxhIGZvciB0aGUgZGVjaXNpb24gdHJlZQ0KbXlGb3JtdWxhIDwtIGBQcmVkaWN0ZWQgSG9iYnlgIH4gU2Nob2xhcnNoaXAgKyBGYXZfc3ViICsgUHJvamVjdHMgKyBHcmFzcF9wb3cgKyBUaW1lX3NwcnQgKyBDYXJlZXJfc3BydCArIEFjdF9zcHJ0ICsgRmFudF9hcnRzICsgV29uX2FydHMgKyBUaW1lX2FydCArIE9seW1waWFkX1BhcnRpY2lwYXRpb24NCg0KIyBDcmVhdGUgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgd2l0aCB0aGUgImluZm9ybWF0aW9uIiBzcGxpdHRpbmcgY3JpdGVyaW9uDQpIb2JieV9EYXRhX2N0cmVlIDwtIHJwYXJ0KG15Rm9ybXVsYSwgZGF0YSA9IHRyYWluRGF0YSwgbWV0aG9kID0gImNsYXNzIiwgcGFybXMgPSBsaXN0KHNwbGl0ID0gImluZm9ybWF0aW9uIikpDQoNCg0KDQojIFByaW50IHRoZSBkZWNpc2lvbiB0cmVlDQpwcmludChIb2JieV9EYXRhX2N0cmVlKQ0KDQojIFBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCg0KcnBhcnQucGxvdChIb2JieV9EYXRhX2N0cmVlKQ0KDQoNCg0KDQpgYGANCg0KKipEZWNpc2lvbiBUcmVlIEFuYWx5c2lzIFVzaW5nIEluZm9ybWF0aW9uIGdhaW4oOTAvMTApOioqDQoNCkluIFRoaXJkIFRyZWUgLHdlIGRldmlkZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgc2V0IGFuZCB0ZXN0IHNldCB3aXRoIHNpemUoJTkwLCUxMCkgcmVzcGVjdGl2ZWx5LiBBcyB5b3UgY2FuIHNlZSBpbiB0aGUgZmlndXJlLCB0aGUgcm9vdCBub2RlICgiQ2FyZWVyX3NwcnQiKSBzZXJ2ZXMgYXMgdGhlIHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgY2xhc3NpZmljYXRpb24gcHJvY2VzcyBzaW5jZSBoYXZlIHRoZSBoZWlnaHRzIEdhaW4uIFRoZSBkYXRhc2V0IGhhcyBhIGRpc3RyaWJ1dGlvbiBvZiBhcHByb3hpbWF0ZWx5IDQzJSBmb3IgY2xhc3MgMSgiQWNhZGVtaWNzIiksIDI1JSBmb3IgY2xhc3MgMigiQXJ0cyIpLCBhbmQgMzIlIGZvciBjbGFzcyAzICgiU3BvcnRzIikuDQoNClRoZSB0cmVlIGZ1cnRoZXIgYnJhbmNoZXMgYmFzZWQgb24gdGhlIHZhbHVlcyBvZiB0aGUgIkNhcmVlcl9zcHJ0IiBpZiBlcXVhbCAwLCB0cmVlIGJyYW5jaGVzIGJhc2VkIG9uIldvbl9hcnRzIiAsYW5kIG1ham9yaXR5IG9mIGluc3RhbmNlcyBmYWxsIGludG8gIkFjYWRpbWNzIiwgY29uc3RpdHV0aW5nIDY1JSAsIGlmIldvbl9hcnRzIiBlcXVhbCAwIG9yIDIgSW4gdGhpcyBjYXNlIHRoZSB0cmVlIHRlcm1pbmF0ZXMgd2l0aCBhIGxlYWYgbm9kZSBpbmRpY2F0aW5nIGEgaGlnaCBwcm9iYWJpbGl0eSA5MCUgZm9yICJBY2FkaW1jcyIuZWxzZSB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGJhc2VkIG9uICJGYW50X2FydHMiaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDg3JSxpZiBlcXVhbCAwIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQWNhZGVtaWNzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNzQlICxlbHNlIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQXJ0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTYlLCBpZiAiQ2FyZWVyX3NwcnQiIGlzIDEsIHRoZSB0cmVlIGZ1cnRoZXIgYnJhbmNoZXMgYmFzZWQgb24gdGhlICJGYW50X2FydHMiIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDc5JS4gSWYgIkZhbnRfYXJ0cyIgaXMgMSAsIHRoZXJlIGlzIGFub3RoZXIgc3BsaXQgYmFzZWQgb24gdGhlICJUaW1lX2FydCIgaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDQ4JS4gSWYgIlRpbWVfYXJ0IiBpcyBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gMywgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgODQlIC4gT24gdGhlIG90aGVyIGhhbmQsIGlmICJUaW1lX2FydCIgaXMgbGVzcyB0aGFuIDMsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgYmFzZWQgb24iQWN0X3NwcnQgIiBpbnRvICJTcG9ydHMiIHdpdGggYSBwcm9iYWJpbGl0eSBvZiA1OCUgLmlmICJBY3Rfc3BydCIgZXF1YWwgMCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIkFjYWRlbWljcyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDU5JSwgb3RoZXIgaGFuZCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDc4JS4gSWYgIkZhbnRfYXJ0cyIgbm90IGVxdWFsIDEgLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTQlLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIGxlYWYgbm9kZS4NCg0KKipUaGlyZCBDb25mdXNpb24gbWF0cml4KioNCg0KYGBge3J9DQoNCiMgUHJlZGljdCBvbiB0aGUgdGVzdCBkYXRhDQp0ZXN0UHJlZCA8LSBwcmVkaWN0KEhvYmJ5X0RhdGFfY3RyZWUsIG5ld2RhdGEgPSB0ZXN0RGF0YSwgdHlwZSA9ICdjbGFzcycpDQoNCiMgQ2hlY2sgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbA0KYWNjdXJhY3kgPC0gc3VtKHRlc3RQcmVkID09IHRlc3REYXRhJGBQcmVkaWN0ZWQgSG9iYnlgKSAvIG5yb3codGVzdERhdGEpICogMTAwDQpjYXQoJ0FjY3VyYWN5OicsIGFjY3VyYWN5LCAnXG4nKQ0KDQojIENyZWF0ZSBhIGNvbmZ1c2lvbiBtYXRyaXgNCmNvbmZfbWF0cml4IDwtIHRhYmxlKEFjdHVhbCA9IHRlc3REYXRhJGBQcmVkaWN0ZWQgSG9iYnlgLCBQcmVkaWN0ZWQgPSB0ZXN0UHJlZCkNCg0KIyBDYWxjdWxhdGUgcHJlY2lzaW9uIGZvciBlYWNoIGNsYXNzDQpwcmVjaXNpb25fY2xhc3NfMSA8LSBjb25mX21hdHJpeFsxLCAxXSAvIHN1bShjb25mX21hdHJpeFsxLCBdKQ0KcHJlY2lzaW9uX2NsYXNzXzIgPC0gY29uZl9tYXRyaXhbMiwgMl0gLyBzdW0oY29uZl9tYXRyaXhbMiwgXSkNCnByZWNpc2lvbl9jbGFzc18zIDwtIGNvbmZfbWF0cml4WzMsIDNdIC8gc3VtKGNvbmZfbWF0cml4WzMsIF0pDQoNCiMgQ2FsY3VsYXRlIHNlbnNpdGl2aXR5IGZvciBlYWNoIGNsYXNzDQpzZW5zaXRpdml0eV9jbGFzc18xIDwtIGNvbmZfbWF0cml4WzEsIDFdIC8gc3VtKGNvbmZfbWF0cml4WzEsIF0pDQpzZW5zaXRpdml0eV9jbGFzc18yIDwtIGNvbmZfbWF0cml4WzIsIDJdIC8gc3VtKGNvbmZfbWF0cml4WzIsIF0pDQpzZW5zaXRpdml0eV9jbGFzc18zIDwtIGNvbmZfbWF0cml4WzMsIDNdIC8gc3VtKGNvbmZfbWF0cml4WzMsIF0pDQoNCiMgQ2FsY3VsYXRlIHNwZWNpZmljaXR5IGZvciBlYWNoIGNsYXNzDQpzcGVjaWZpY2l0eV9jbGFzc18xIDwtIHN1bShkaWFnKGNvbmZfbWF0cml4Wy0xLCAtMV0pKSAvIHN1bShjb25mX21hdHJpeFstMSwgXSkNCnNwZWNpZmljaXR5X2NsYXNzXzIgPC0gc3VtKGNvbmZfbWF0cml4W2MoMSwgMyksIGMoMSwgMyldKSAvIHN1bShjb25mX21hdHJpeFtjKDEsIDMpLCBdKQ0Kc3BlY2lmaWNpdHlfY2xhc3NfMyA8LSBzdW0oY29uZl9tYXRyaXhbYygxLCAyKSwgYygxLCAyKV0pIC8gc3VtKGNvbmZfbWF0cml4W2MoMSwgMiksIF0pDQoNCiMgQ2FsY3VsYXRlIG1hY3JvLWF2ZXJhZ2Ugc2Vuc2l0aXZpdHkNCm1hY3JvX2F2Z19zZW5zaXRpdml0eSA8LSAoc2Vuc2l0aXZpdHlfY2xhc3NfMSArIHNlbnNpdGl2aXR5X2NsYXNzXzIgKyBzZW5zaXRpdml0eV9jbGFzc18zKSAvIDMNCg0KIyBDYWxjdWxhdGUgbWFjcm8tYXZlcmFnZSBzcGVjaWZpY2l0eQ0KbWFjcm9fYXZnX3NwZWNpZmljaXR5IDwtIChzcGVjaWZpY2l0eV9jbGFzc18xICsgc3BlY2lmaWNpdHlfY2xhc3NfMiArIHNwZWNpZmljaXR5X2NsYXNzXzMpIC8gMw0KDQojIENhbGN1bGF0ZSBtYWNyby1hdmVyYWdlIHByZWNpc2lvbg0KbWFjcm9fYXZnX3ByZWNpc2lvbiA8LSAocHJlY2lzaW9uX2NsYXNzXzEgKyBwcmVjaXNpb25fY2xhc3NfMiArIHByZWNpc2lvbl9jbGFzc18zKSAvIDMNCg0KIyBQcmludCBtYWNyby1hdmVyYWdlIHNlbnNpdGl2aXR5DQpjYXQoJ0F2ZXJhZ2UgU2Vuc2l0aXZpdHk6JywgbWFjcm9fYXZnX3NlbnNpdGl2aXR5LCAnXG4nKQ0KDQojIFByaW50IG1hY3JvLWF2ZXJhZ2Ugc3BlY2lmaWNpdHkNCmNhdCgnQXZlcmFnZSBTcGVjaWZpY2l0eTonLCBtYWNyb19hdmdfc3BlY2lmaWNpdHksICdcbicpDQoNCiMgUHJpbnQgbWFjcm8tYXZlcmFnZSBwcmVjaXNpb24NCmNhdCgnQXZlcmFnZSBQcmVjaXNpb246JywgbWFjcm9fYXZnX3ByZWNpc2lvbiwgJ1xuJykNCg0KDQoNCg0KDQoNCiMgUHJpbnQgcHJlY2lzaW9uIGZvciBlYWNoIGNsYXNzDQpjYXQoJ1ByZWNpc2lvbiBmb3IgQ2xhc3MgMTonLCBwcmVjaXNpb25fY2xhc3NfMSwgJyBcbicpDQoNCmNhdCgnUHJlY2lzaW9uIGZvciBDbGFzcyAyOicsIHByZWNpc2lvbl9jbGFzc18yLCAnICBcbicpDQpjYXQoJ1ByZWNpc2lvbiBmb3IgQ2xhc3MgMzonLCBwcmVjaXNpb25fY2xhc3NfMywgJyBcbicpDQoNCiMgUHJpbnQgc2Vuc2l0aXZpdHkgZm9yIGVhY2ggY2xhc3MNCmNhdCgnU2Vuc2l0aXZpdHkgZm9yIENsYXNzIDE6Jywgc2Vuc2l0aXZpdHlfY2xhc3NfMSwgJ1xuJykNCmNhdCgnU2Vuc2l0aXZpdHkgZm9yIENsYXNzIDI6Jywgc2Vuc2l0aXZpdHlfY2xhc3NfMiwgJ1xuJykNCmNhdCgnU2Vuc2l0aXZpdHkgZm9yIENsYXNzIDM6Jywgc2Vuc2l0aXZpdHlfY2xhc3NfMywgJ1xuJykNCg0KIyBQcmludCBzcGVjaWZpY2l0eSBmb3IgZWFjaCBjbGFzcw0KY2F0KCdTcGVjaWZpY2l0eSBmb3IgQ2xhc3MgMTonLCBzcGVjaWZpY2l0eV9jbGFzc18xLCAnXG4nKQ0KY2F0KCdTcGVjaWZpY2l0eSBmb3IgQ2xhc3MgMjonLCBzcGVjaWZpY2l0eV9jbGFzc18yLCAnXG4nKQ0KY2F0KCdTcGVjaWZpY2l0eSBmb3IgQ2xhc3MgMzonLCBzcGVjaWZpY2l0eV9jbGFzc18zLCAnXG4nKQ0KDQoNCmBgYA0KDQoqKkNvbXBhcmluZyBEZWNpc2lvbiBUcmVlIFJlc3VsdHMgVXNpbmcgSW5mcm9tYXRpb24gZ2FpbjoqKg0KQWZ0ZXIgdHJhaW5pbmcgdGhyZWUgdHJlZXMgd2l0aCBkaWZmZXJlbnQgc2l6ZXMsIGVtcGxveWluZyBpbmZvcm1hdGlvbiBnYWluIGFzIHRoZSBzZWxlY3Rpb24gbWVhc3VyZSwgb3VyIGFuYWx5c2lzIGxlZCB0byBjb25zaXN0ZW50IGFjY3VyYWN5IHJlc3VsdHMgYW1vbmcgdGhlIHRyZWVzOiBUcmVlIDEgKDAuODkzMiksIFRyZWUgMiAoMC45MDU3KSwgYW5kIFRyZWUgMyAoMC44NzIxKS4gVGhlIG1pbm9yIGRpc2NyZXBhbmNpZXMgb2JzZXJ2ZWQgaW4gdGhlc2UgYWNjdXJhY3kgdmFsdWVzIGNvdWxkIGJlIGF0dHJpYnV0ZWQgdG8gdGhlIHZhcmlhdGlvbnMgaW4gZGF0YXNldCBzaXplcy4gSW52ZXN0aWdhdGluZyB0aGUgaW1wYWN0IG9mIGRpZmZlcmVudCB0cmFpbmluZyBzZXQgc2l6ZXMgb24gbW9kZWwgcGVyZm9ybWFuY2Ugb2ZmZXJzIHZhbHVhYmxlIGluc2lnaHRzIGludG8gdGhlIGludHJpY2F0ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBkYXRhIHNpemUgYW5kIGFjY3VyYWN5Lg0KDQpJbiB0aGUgY2FzZSBvZiBUcmVlIDIsIHdoZXJlIGEgbGFyZ2VyIHRyYWluaW5nIHNldCB3YXMgZW1wbG95ZWQgKDgwJSB0cmFpbmluZywgMjAlIHRlc3RpbmcpLCB0aGUgbW9kZWwgaGFkIHRoZSBvcHBvcnR1bml0eSB0byBncmFzcCBtb3JlIHJvYnVzdCBwYXR0ZXJucyBhbmQgcmVsYXRpb25zaGlwcyB3aXRoaW4gdGhlIGRhdGEuIEhvd2V2ZXIsIGl0IGlzIGNydWNpYWwgdG8gdW5kZXJzY29yZSB0aGUgbmVjZXNzaXR5IG9mIHN0cmlraW5nIGEgYmFsYW5jZSBiZXR3ZWVuIHRoZSBzaXplcyBvZiB0aGUgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cy4gRm9yIFRyZWUgMywgd2l0aCBhIHJlbGF0aXZlbHkgc21hbGxlciB0ZXN0aW5nIHNldCAoOTAlIHRyYWluaW5nLCAxMCUgdGVzdGluZyksIHRoZSBhY2N1cmFjeSBlc3RpbWF0ZSBtaWdodCBiZSBsZXNzIHJlbGlhYmxlIGR1ZSB0byB0aGUgbGltaXRlZCBzYW1wbGUgc2l6ZSBpbiB0aGUgdGVzdGluZyBzZXQuDQoNCkluIHN1bW1hcnksIHRoZSB1dGlsaXphdGlvbiBvZiBpbmZvcm1hdGlvbiBnYWluIGFzIHRoZSBzZWxlY3Rpb24gbWVhc3VyZSwgY291cGxlZCB3aXRoIGRpZmZlcmVudCB0cmFpbmluZyBzZXQgc2l6ZXMsIHJlc3VsdGVkIGluIGNvbXBhcmFibGUgYWNjdXJhY3kgb3V0Y29tZXMuIEFjaGlldmluZyBhbiBvcHRpbWFsIGJhbGFuY2UgaW4gdGhlIHNpemVzIG9mIGJvdGggdHJhaW5pbmcgYW5kIHRlc3RpbmcgZGF0YXNldHMgcHJvdmVzIGVzc2VudGlhbCBmb3IgZW5zdXJpbmcgYWNjdXJhdGUgYW5kIGdlbmVyYWxpemFibGUgbW9kZWwgcGVyZm9ybWFuY2UuDQorLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBpbmZvcm1hdGlvbiBnYWluIHwgOTAgJXQgcmFpbmluZyBzZXQgfCA4MCAldCByYWluaW5nIHNldCB8IDcwICV0IHJhaW5pbmcgc2V0IHwNCnwgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgfCAxMCUgdGVzdGluZyBzZXQ6ICB8IDIwJSB0ZXN0aW5nIHNldDogIHwgMzAlIHRlc3Rpbmcgc2V0OiAgfA0KKzo9PT09PT09PT09PT09PT09Ois6PT09PT09PT09PT09PT09PT06Kzo9PT09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09PT09OisNCnwgKipBY2N1cmFjeSoqICAgICB8IDAuODcyICAgICAgICAgICAgIHwgMC45MDUgICAgICAgICAgICAgfCAwLjg5MyAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAqKnByZWNpc2lvbioqICAgIHwgMC44NTYgICAgICAgICAgICAgfCAwLjg5OCAgICAgICAgICAgICB8IDAuODg3ICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqc2Vuc2l0aXZpdHkqKiAgfCAwLjg1NiAgICAgICAgICAgICB8IDAuODk4ICAgICAgICAgICAgIHwgMC44ODcgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKipzcGVjaWZpY2l0eSoqICB8IDAuOTE5ICAgICAgICAgICAgIHwgMC45MzcgICAgICAgICAgICAgfCAwLjkyOSAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQoNCioqR2luaSBpbmRleCoqDQoNClRoZSBHaW5pIGluZGV4LCBpcyBhIG1lYXN1cmUgdXNlZCBpbiBkZWNpc2lvbiB0cmVlcywgc3BlY2lmaWNhbGx5IGluIHRoZSBDQVJUIChDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbiBUcmVlcykgYWxnb3JpdGhtLCB0byBxdWFudGlmeSBob3cgb2Z0ZW4gYSByYW5kb21seSBjaG9zZW4gZWxlbWVudCB3b3VsZCBiZSBpbmNvcnJlY3RseSBsYWJlbGVkIGlmIGl0IHdhcyByYW5kb21seSBsYWJlbGVkIGFjY29yZGluZyB0byB0aGUgZGlzdHJpYnV0aW9uIG9mIGxhYmVscyBpbiB0aGUgc3Vic2V0LiBJdCByZWZsZWN0cyB0aGUgcHJvYmFiaWxpdHkgb2YgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlIGJlaW5nIHdyb25nbHkgY2xhc3NpZmllZCB3aGVuIGl0IGlzIHJhbmRvbWx5IGNob3Nlbi4NCg0KKioxLUdpbmkgaW5kZXgoODAlLDIwJSkqKg0KDQpJbnN0YWxsIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJycGFydCIpDQppbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikNCmluc3RhbGwucGFja2FnZXMoImNhVG9vbHMiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KDQpgYGANCg0KTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FUb29scykNCmxpYnJhcnkoY2FyZXQpDQpgYGANCg0KU2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KDQpgYGANCg0KU3BsaXQgdGhlIGRhdGFzZXQsIDgwJSBmb3IgdHJhaW5pbmcsIDIwJSBmb3IgdGVzdGluZw0KDQpgYGB7cn0NCnNwbGl0IDwtIHNhbXBsZS5zcGxpdChIb2JieV9EYXRhJGBQcmVkaWN0ZWQgSG9iYnlgLCBTcGxpdFJhdGlvID0gMC44MCkNCmBgYA0KDQpDcmVhdGUgdGhlIHRyYWluaW5nIHNldCAoODAlIG9mIHRoZSBkYXRhKQ0KDQpgYGB7cn0NCnRyYWluaW5nX3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gVFJVRSkNCmBgYA0KDQpDcmVhdGUgdGhlIHRlc3Qgc2V0ICgyMCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdGVzdF9zZXQgPC0gc3Vic2V0KEhvYmJ5X0RhdGEsIHNwbGl0ID09IEZBTFNFKQ0KDQpgYGANCg0KQnVpbGQgYSBkZWNpc2lvbiB0cmVlIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBzZXQNCg0KYGBge3J9DQp0cmVlIDwtIHJwYXJ0KGBQcmVkaWN0ZWQgSG9iYnlgIH4gLiwgZGF0YSA9IHRyYWluaW5nX3NldCwgbWV0aG9kID0gJ2NsYXNzJykNCg0KYGBgDQoNCg0KTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQgdXNpbmcgdGhlIHRyZWUgbW9kZWwNCg0KYGBge3J9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHRyZWUsIHRlc3Rfc2V0LCB0eXBlID0gImNsYXNzIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4DQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IHRlc3Rfc2V0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBhY2N1cmFjeQ0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQoNCmBgYA0KDQpQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCmBgYHtyfQ0KcnBhcnQucGxvdCh0cmVlKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHaW5pIEluZGV4KDgwLzIwKToqKg0KIFRoZSBkZWNpc2lvbiB0cmVlIGRlbGluZWF0ZXMgaG9iYmllcyBpbnRvICdBY2FkZW1pY3MnICgxKSwgJ0FydHMnICgyKSwgYW5kICdTcG9ydHMnICgzKS4gV2l0aG91dCBhIHNwb3J0cyBob2JieSAoJ0NhcmVlcl9zcHJ0JyA9IDApLCB0aGUgbW9kZWwgc3VnZ2VzdHMgYSA2MiUgY2hhbmNlIG9mICdBY2FkZW1pY3MnLiBXaXRoIG5vIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMCkgYW5kICdXb25fYXJ0cycgYXQgMCBvciAyLCB0aGVyZSdzIGEgNDMlIGNoYW5jZSBvZiBhbiAnQWNhZGVtaWNzJyBjYXRlZ29yaXphdGlvbi4gQ29udmVyc2VseSwgZm9yIHRob3NlIHdpdGggYW4gYXJ0cyBob2JieSAoJ0ZhbnRfYXJ0cycgPSAxKSBhbmQgZnJlcXVlbnQgYXJ0cyBhY3Rpdml0aWVzICgnVGltZV9hcnQnIOKJpSAzKSwgVGhlc2UgbW9kZWwgc2hvdyBob3cgbGlrZWx5IHRoZSBtb2RlbCBpcyB0byBwcmVkaWN0IGVhY2ggaG9iYnkgYmFzZWQgb24gdGhlIGF0dHJpYnV0ZXMnIHNpZ25pZmljYW5jZSwgYXMgbGVhcm5lZCBmcm9tIHRoZSBkYXRhIHdpdGggYSA4MCUgdHJhaW5pbmcgcG9ydGlvbg0KDQoqKjItR2luaSBpbmRleCg5MCUsMTAlKSoqDQoNCkluc3RhbGwgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FUb29scyIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQoNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQoNCmBgYA0KDQpTcGxpdCB0aGUgZGF0YXNldCwgOTAlIGZvciB0cmFpbmluZywgMTAlIGZvciB0ZXN0aW5nDQoNCmBgYHtyfQ0Kc3BsaXQgPC0gc2FtcGxlLnNwbGl0KEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIFNwbGl0UmF0aW8gPSAwLjkwKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdHJhaW5pbmcgc2V0ICg5MCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdHJhaW5pbmdfc2V0IDwtIHN1YnNldChIb2JieV9EYXRhLCBzcGxpdCA9PSBUUlVFKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdGVzdCBzZXQgKDEwJSBvZiB0aGUgZGF0YSkNCg0KYGBge3J9DQp0ZXN0X3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gRkFMU0UpDQoNCmBgYA0KDQpCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldA0KDQpgYGB7cn0NCnRyZWUgPC0gcnBhcnQoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gdHJhaW5pbmdfc2V0LCBtZXRob2QgPSAnY2xhc3MnKQ0KDQpgYGANCg0KDQpNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldCB1c2luZyB0aGUgdHJlZSBtb2RlbA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QodHJlZSwgdGVzdF9zZXQsIHR5cGUgPSAiY2xhc3MiKQ0KYGBgDQoNCkNvbmZ1c2lvbiBtYXRyaXgNCg0KYGBge3J9DQpjb25mX21hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0aW9ucywgQWN0dWFsID0gdGVzdF9zZXQkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KQ2FsY3VsYXRlIGFjY3VyYWN5DQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCmBgYA0KDQpJbml0aWFsaXplIHZlY3RvcnMgdG8gaG9sZCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCnByZWNpc2lvbiA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KcmVjYWxsIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpzcGVjaWZpY2l0eSA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KYGBgDQoNCkNhbGN1bGF0ZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6bnJvdyhjb25mX21hdHJpeCkpIHsNCiAgVFAgPC0gY29uZl9tYXRyaXhbaSwgaV0NCiAgRlAgPC0gc3VtKGNvbmZfbWF0cml4WywgaV0pIC0gVFANCiAgRk4gPC0gc3VtKGNvbmZfbWF0cml4W2ksIF0pIC0gVFANCiAgVE4gPC0gc3VtKGNvbmZfbWF0cml4KSAtIFRQIC0gRlAgLSBGTg0KICANCiAgcHJlY2lzaW9uW2ldIDwtIFRQIC8gKFRQICsgRlApDQogIHJlY2FsbFtpXSA8LSBUUCAvIChUUCArIEZOKQ0KICBzcGVjaWZpY2l0eVtpXSA8LSBUTiAvIChUTiArIEZQKQ0KfQ0KYGBgDQoNCkF2ZXJhZ2UgdGhlIG1ldHJpY3MgaWYgeW91IHdhbnQgYSBzaW5nbGUgcGVyZm9ybWFuY2UgbWVhc3VyZQ0KDQpgYGB7cn0NCmF2Z19wcmVjaXNpb24gPC0gbWVhbihwcmVjaXNpb24pDQphdmdfcmVjYWxsIDwtIG1lYW4ocmVjYWxsKQ0KYXZnX3NwZWNpZmljaXR5IDwtIG1lYW4oc3BlY2lmaWNpdHkpDQpgYGANCg0KT3V0cHV0IHRoZSBldmFsdWF0aW9uIG1ldHJpY3MNCg0KYGBge3J9DQpwcmludChwYXN0ZSgiT3ZlcmFsbCBBY2N1cmFjeToiLCBhY2N1cmFjeSkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBQcmVjaXNpb246IiwgYXZnX3ByZWNpc2lvbikpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBSZWNhbGwgKFNlbnNpdGl2aXR5KToiLCBhdmdfcmVjYWxsKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFNwZWNpZmljaXR5OiIsIGF2Z19zcGVjaWZpY2l0eSkpDQpgYGANCg0KdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3M6DQoNCmBgYHtyfQ0KbWV0cmljcyA8LSBkYXRhLmZyYW1lKA0KICBDbGFzcyA9IHJvd25hbWVzKGNvbmZfbWF0cml4KSwNCiAgUHJlY2lzaW9uID0gcHJlY2lzaW9uLA0KICBSZWNhbGwgPSByZWNhbGwsDQogIFNwZWNpZmljaXR5ID0gc3BlY2lmaWNpdHkNCikNCmBgYA0KDQpQcmludCBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQobWV0cmljcykNCg0KYGBgDQoNCg0KUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpgYGB7cn0NCnJwYXJ0LnBsb3QodHJlZSkNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgR2luaSBJbmRleCg5MC8xMCk6KioNCg0KVGhlIGRlY2lzaW9uIHRyZWUgY2xhc3NpZmllcyBob2JiaWVzIGludG8gJ0FjYWRlbWljcycgKDEpLCAnQXJ0cycgKDIpLCBhbmQgJ1Nwb3J0cycgKDMpLiBBIGxhY2sgb2YgYSBzcG9ydHMgaG9iYnkgKCdDYXJlZXJfc3BydCcgPSAwKSBsZWFkcyB0byBhIDYzJSBjaGFuY2Ugb2YgZmFsbGluZyBpbnRvICdBY2FkZW1pY3MnLiBJZiBzb21lb25lIGlzIG5vdCBlbmdhZ2VkIGluIGFuIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMCkgYW5kICdXb25fYXJ0cycgaXMgMCBvciAyLCB0aGVyZSdzIGEgNDMlIHByb2JhYmlsaXR5IG9mIGFuICdBY2FkZW1pY3MnIGNhdGVnb3JpemF0aW9uLiBGb3IgaW5kaXZpZHVhbHMgZW5nYWdlZCBpbiBhbiBhcnRzIGhvYmJ5ICgnRmFudF9hcnRzJyA9IDEpIHdpdGggYSBoaWdoIGxldmVsIG9mIGFydHMgYWN0aXZpdHkgKCdUaW1lX2FydCcg4omlIDMpLCB0aGUgbGlrZWxpaG9vZCBvZiBhICdTcG9ydHMnIGNsYXNzaWZpY2F0aW9uIGlzIDI4JS4gVGhlc2UgbW9kZWwgc2hvdyBob3cgbGlrZWx5IHRoZSBtb2RlbCBpcyB0byBwcmVkaWN0IGVhY2ggaG9iYnkgYmFzZWQgb24gdGhlIGF0dHJpYnV0ZXMnIHNpZ25pZmljYW5jZSwgYXMgbGVhcm5lZCBmcm9tIHRoZSBkYXRhIHdpdGggYSA5MCUgdHJhaW5pbmcgcG9ydGlvbi4NCg0KDQoqKjMtR2luaSBpbmRleCg3MCUsMzAlKSoqDQoNCkluc3RhbGwgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FUb29scyIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQoNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQoNCmBgYA0KDQpTcGxpdCB0aGUgZGF0YXNldCwgNzAlIGZvciB0cmFpbmluZywgMzAlIGZvciB0ZXN0aW5nDQoNCmBgYHtyfQ0Kc3BsaXQgPC0gc2FtcGxlLnNwbGl0KEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIFNwbGl0UmF0aW8gPSAwLjcwKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdHJhaW5pbmcgc2V0ICg3MCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdHJhaW5pbmdfc2V0IDwtIHN1YnNldChIb2JieV9EYXRhLCBzcGxpdCA9PSBUUlVFKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdGVzdCBzZXQgKDIwJSBvZiB0aGUgZGF0YSkNCg0KYGBge3J9DQp0ZXN0X3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gRkFMU0UpDQoNCmBgYA0KDQpCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldA0KDQpgYGB7cn0NCnRyZWUgPC0gcnBhcnQoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gdHJhaW5pbmdfc2V0LCBtZXRob2QgPSAnY2xhc3MnKQ0KDQpgYGANCg0KTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQgdXNpbmcgdGhlIHRyZWUgbW9kZWwNCg0KYGBge3J9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHRyZWUsIHRlc3Rfc2V0LCB0eXBlID0gImNsYXNzIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4DQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IHRlc3Rfc2V0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBhY2N1cmFjeQ0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQoNCmBgYA0KDQpQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCmBgYHtyfQ0KcnBhcnQucGxvdCh0cmVlKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHaW5pIEluZGV4KDcwLzMwKToqKg0KDQpUaGUgZGVjaXNpb24gdHJlZSBzb3J0cyBob2JiaWVzIGludG8gJ0FjYWRlbWljcycgKDEpLCAnQXJ0cycgKDIpLCBhbmQgJ1Nwb3J0cycgKDMpLiBBIG5vbi1zcG9ydHMgaG9iYnkgKCdDYXJlZXJfc3BydCcgPSAwKSByZXN1bHRzIGluIGEgNjMlIHByb2JhYmlsaXR5IG9mIGFuICdBY2FkZW1pY3MnIGNhdGVnb3JpemF0aW9uLiBJZiAnRmFudF9hcnRzJyBpcyAwIGFuZCAnV29uX2FydHMnIGlzIDAgb3IgMiwgdGhlcmUncyBhIDQzJSBjaGFuY2Ugb2YgYmVpbmcgY2xhc3NpZmllZCBhcyAnQWNhZGVtaWNzJy4gQ29udmVyc2VseSwgZm9yIHRob3NlIGludm9sdmVkIGluIGFuIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMSkgd2l0aCBzaWduaWZpY2FudCBhcnRzIGFjdGl2aXR5ICgnVGltZV9hcnQnIOKJpSAzKSwgdGhlIG1vZGVsIGluZGljYXRlcyBhIDI4JSBwcm9iYWJpbGl0eSBvZiBhICdTcG9ydHMnIGhvYmJ5LiBUaGlzIGRlY2lzaW9uIHRyZWUgZGVtb25zdHJhdGVzIHRoZSBsaWtlbGlob29kIG9mIHByZWRpY3RpbmcgZWFjaCBob2JieSBiYXNlZCBvbiB0aGUgaW1wb3J0YW5jZSBvZiB0aGUgYXR0cmlidXRlcywgYXMgZGV0ZXJtaW5lZCBmcm9tIHRoZSBkYXRhIHRyYWluZWQgd2l0aCBhIDcwJSBwb3J0aW9uLg0KDQoqKkNvbXBhcmluZyBEZWNpc2lvbiBUcmVlIFJlc3VsdHMgVXNpbmcgR2luaSBJbmRleDoqKg0KDQpBY3Jvc3MgVGhyZWUgVHJhaW5pbmctVGVzdCBTaXplczogVGhlIHJlc3VsdHMgb2YgdGhlIGRlY2lzaW9uIHRyZWVzIGZyb20gdGhlIDkwLzEwLCA4MC8yMCwgYW5kIDcwLzMwIGRhdGFzZXQgc3BsaXRzLCB0aGVyZSBpcyBhIGNvbnNpc3RlbnQgcGF0dGVybjogJ0NhcmVlcl9zcHJ0JyBpcyBhbHdheXMgdGhlIHJvb3Qgbm9kZSwgYW5kIHRoZSBzdWJzZXF1ZW50IHNwbGl0cyBvbiAnV29uX2FydHMnIGFuZCAnRmFudF9hcnRzJyBhcmUgdGhlIHNhbWUgYWNyb3NzIGFsbCB0cmVlcy4gVGhpcyBjb25zaXN0ZW5jeSBpbiB0cmVlIHN0cnVjdHVyZSBhbmQgdGhlIHByb2JhYmlsaXRpZXMgZm9yIHByZWRpY3RpbmcgJ0FjYWRlbWljcycgYW5kICdTcG9ydHMnIGFjcm9zcyBkaWZmZXJlbnQgc3BsaXRzIHN1Z2dlc3QgYSBzdGFibGUgYW5kIHJvYnVzdCBtb2RlbCB0aGF0IGlzIHJlbGlhYmxlIHJlZ2FyZGxlc3Mgb2YgdGhlIHRyYWluaW5nIHNldCBzaXplLg0KDQp0aGUgYWNjdXJhY2llcyBvZiB0aHJlZSBkYXRhIHNwbGl0cyByZXZlYWxzIGRpc3RpbmN0IG91dGNvbWVzOiB0aGUgKDkwLDEwKSBzcGxpdCBsZWFkcyB3aXRoIHRoZSBoaWdoZXN0IGFjY3VyYWN5IGF0IDAuOTE4NzUsIHN1Z2dlc3RpbmcgdGhhdCBhIGxhcmdlciB0cmFpbmluZyBwb3J0aW9uIGlzIG1vcmUgZWZmZWN0aXZlIGluIHRoaXMgY2FzZS4gVGhlICg3MCwzMCkgc3BsaXQgZm9sbG93cyB3aXRoIGFuIGFjY3VyYWN5IG9mIDAuOTEwNjAsIHNob3dpbmcgc3Ryb25nIHBlcmZvcm1hbmNlIGV2ZW4gd2l0aCBhIGxhcmdlciB0ZXN0IHNldC4gSG93ZXZlciwgdGhlIGNvbW1vbmx5IHVzZWQgKDgwLDIwKSBzcGxpdCBsYWdzIHNsaWdodGx5IGJlaGluZCwgYWNoaWV2aW5nIGFuIGFjY3VyYWN5IG9mIDAuOTA2MjUuIFRoaXMgY29tcGFyaXNvbiBoaWdobGlnaHRzIHRoZSBpbXBhY3Qgb2YgdmFyeWluZyB0cmFpbmluZyBhbmQgdGVzdGluZyBwcm9wb3J0aW9ucyBvbiBtb2RlbCBhY2N1cmFjeS4NCg0KDQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEdpbmkgaW5kZXggICAgICB8IDkwICV0IHJhaW5pbmcgc2V0IDEwJSB0ZXN0aW5nIHNldDogfCA4MCAldCByYWluaW5nIHNldCAyMCUgdGVzdGluZyBzZXQ6IHwgNzAgJXQgcmFpbmluZyBzZXQgMzAlIHRlc3Rpbmcgc2V0OiB8DQorOj09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT06Kzo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ois6PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PTorDQp8ICoqQWNjdXJhY3kqKiAgICB8IDAuOTE4NzUgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjkwNiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMC45MTEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqcHJlY2lzaW9uKiogICB8IDAuOTE4NTAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjkwNSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMC45MDkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqc2Vuc2l0aXZpdHkqKiB8IDAuOTE5MjcgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjkwOSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMC45MTIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqc3BlY2lmaWNpdHkqKiB8IDAuOTU3OCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjk1MiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMC45NTQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCg0KKipHYWluIFJhdGlvKioNClRoZSB0aGlyZCBjcml0ZXJpb24gZW1wbG95ZWQgZm9yIGJ1aWxkaW5nIHRoZSBkZWNpc2lvbiB0cmVlIGlzIEdhaW4gUmF0aW8uIEdhaW4gUmF0aW8gc3RhbmRzIG91dCBhcyBhIHNpZ25pZmljYW50IG1ldHJpYyBpbiBkZWNpc2lvbiB0cmVlIGFsZ29yaXRobXMsIGVzcGVjaWFsbHkgaW4gc2NlbmFyaW9zIGludm9sdmluZyBjYXRlZ29yaWNhbCB0YXJnZXQgdmFyaWFibGVzLiBJdCBub3JtYWxpemVzIHRoZSByZWR1Y3Rpb24gaW4gZW50cm9weSBieSB0YWtpbmcgaW50byBhY2NvdW50IHRoZSBwb3RlbnRpYWwgaW5mb3JtYXRpb24gY29udGVudCBvZiB0aGUgZmVhdHVyZS4gVGhpcyBub3JtYWxpemF0aW9uIHByb2Nlc3MgbWFrZXMgR2FpbiBSYXRpbyBwYXJ0aWN1bGFybHkgc3VpdGFibGUgZm9yIGRhdGFzZXRzIHdpdGggY2F0ZWdvcmljYWwgdGFyZ2V0IHZhcmlhYmxlcy4gQnkgZmFjdG9yaW5nIGluIHRoZSBpbnRyaW5zaWMgaW5mb3JtYXRpb24gb2YgYSBzcGxpdCwgR2FpbiBSYXRpbyBlZmZlY3RpdmVseSBtaXRpZ2F0ZXMgYmlhcyB0b3dhcmRzIGZlYXR1cmVzIHdpdGggaGlnaGVyIGxldmVscywgZW5zdXJpbmcgYSBtb3JlIGJhbGFuY2VkIGV2YWx1YXRpb24gb2YgZGlmZmVyZW50IGF0dHJpYnV0ZXMuDQoNCioqMS1HYWluIHJhdGlvKDkwJSwxMCUpKioNCg0KSW5zdGFsbCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiQzUwIikNCmluc3RhbGwucGFja2FnZXMoInByaW50ciIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpgYGANCg0KTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KEM1MCkNCmxpYnJhcnkocHJpbnRyKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxOTU4KQ0KDQpgYGANCg0KU3BsaXR0aW5nIHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cw0KDQpgYGB7cn0NCnRyYWluX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhIb2JieV9EYXRhKSwgMC45ICogbnJvdyhIb2JieV9EYXRhKSkNCkhvYmJ5LnRyYWluIDwtIEhvYmJ5X0RhdGFbdHJhaW5faW5kaWNlcywgXQ0KSG9iYnkudGVzdCA8LSBIb2JieV9EYXRhWy10cmFpbl9pbmRpY2VzLCBdDQoNCmBgYA0KDQpUcmFpbmluZyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbA0KDQpgYGB7cn0NCm1vZGVsIDwtIEM1LjAoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gSG9iYnkudHJhaW4sIGNvbnRyb2wgPSBDNS4wQ29udHJvbChDRiA9IDAuMDEpKQ0KDQpgYGANCg0KTWFraW5nIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBIb2JieS50ZXN0LCB0eXBlID0gJ2NsYXNzJykNCmBgYA0KDQpDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4IGZyb20gdGhlIHByZWRpY3Rpb25zIGFuZCBhY3R1YWwgdmFsdWVzDQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IEhvYmJ5LnRlc3QkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KQ2FsY3VsYXRlIGFuZCBwcmludCB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCnByaW50KHBhc3RlKCdBY2N1cmFjeSBvbiB0ZXN0IGRhdGEgaXM6JywgYWNjdXJhY3kpKQ0KYGBgDQoNCkluaXRpYWxpemUgdmVjdG9ycyB0byBob2xkIHRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KcHJlY2lzaW9uIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpyZWNhbGwgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnNwZWNpZmljaXR5IDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpgYGANCg0KQ2FsY3VsYXRlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpmb3IgKGkgaW4gMTpucm93KGNvbmZfbWF0cml4KSkgew0KICBUUCA8LSBjb25mX21hdHJpeFtpLCBpXQ0KICBGUCA8LSBzdW0oY29uZl9tYXRyaXhbLCBpXSkgLSBUUA0KICBGTiA8LSBzdW0oY29uZl9tYXRyaXhbaSwgXSkgLSBUUA0KICBUTiA8LSBzdW0oY29uZl9tYXRyaXgpIC0gVFAgLSBGUCAtIEZODQogIA0KICBwcmVjaXNpb25baV0gPC0gVFAgLyAoVFAgKyBGUCkNCiAgcmVjYWxsW2ldIDwtIFRQIC8gKFRQICsgRk4pDQogIHNwZWNpZmljaXR5W2ldIDwtIFROIC8gKFROICsgRlApDQp9DQpgYGANCg0KQXZlcmFnZSB0aGUgbWV0cmljcyBpZiB5b3Ugd2FudCBhIHNpbmdsZSBwZXJmb3JtYW5jZSBtZWFzdXJlDQoNCmBgYHtyfQ0KYXZnX3ByZWNpc2lvbiA8LSBtZWFuKHByZWNpc2lvbikNCmF2Z19yZWNhbGwgPC0gbWVhbihyZWNhbGwpDQphdmdfc3BlY2lmaWNpdHkgPC0gbWVhbihzcGVjaWZpY2l0eSkNCmBgYA0KDQpPdXRwdXQgdGhlIGV2YWx1YXRpb24gbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KHBhc3RlKCJPdmVyYWxsIEFjY3VyYWN5OiIsIGFjY3VyYWN5KSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFByZWNpc2lvbjoiLCBhdmdfcHJlY2lzaW9uKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFJlY2FsbCAoU2Vuc2l0aXZpdHkpOiIsIGF2Z19yZWNhbGwpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgU3BlY2lmaWNpdHk6IiwgYXZnX3NwZWNpZmljaXR5KSkNCmBgYA0KDQpwcmludCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzczoNCg0KYGBge3J9DQoNCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQpgYGANCg0KR2VuZXJhdGUgYW5kIHByaW50IGFkZGl0aW9uYWwgcGVyZm9ybWFuY2UgbWV0cmljcyB1c2luZyBjYXJldCBwYWNrYWdlDQoNCmBgYHtyfQ0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zLCBIb2JieS50ZXN0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNClBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCg0KYGBge3J9DQpwbG90KG1vZGVsKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHYWluIFJhdGlvKDkwJS8xMCUpOioqDQoNCkluIEZpcnN0IFRyZWUgLHdlIGRldmlkZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgc2V0IGFuZCB0ZXN0IHNldCB3aXRoIHNpemUoJTkwLCUxMCkgcmVzcGVjdGl2ZWx5LiBBcyB5b3UgY2FuIHNlZSBpbiB0aGUgZmlndXJlLCB0aGUgcm9vdCBub2RlIGlzICJDYXJlZXJfc3BydCIgLCBjbGFzcyAxKCJBY2FkZW1pY3MiKSwgY2xhc3MgMigiQXJ0cyIpLCBhbmQgY2xhc3MgMyAoIlNwb3J0cyIpLg0KDQpOb2RlICJDYXJlZXJfc3BydCIgVGhlIGZpcnN0IGRlY2lzaW9uIGlzIGJhc2VkIG9uIHdoZXRoZXIgdGhlIHZhbHVlIG9mIHRoZSAiQ2FyZWVyX3NwcnQiIGF0dHJpYnV0ZSBpcyAwLiB0aGVuIGNoZWNrIGlmICJXb25fYXJ0cyIgaXMgZWl0aGVyIDAgb3IgMi5JZiIgV29uX2FydHMiIGlzIDAgb3IgMiAsIHByZWRpY3QiQWNhZGVtaWMiLnRoZW4gY2hlY2sgSWYgIkZhbnRfYXJ0cyIgaXMgMSAiYW5kIFdvbl9hcnRzIiBpcyAxLCBwcmVkaWN0ICJBcnRzIi5JZiAiRmFudF9hcnRzIiBpcyAwIGFuZCBXb25fYXJ0cyBpcyAwIG9yIDIsIHRoZW4gY2hlY2sgaWYgT2x5bXBpYWRfUGFydGljaXBhdGlvbiBpcyAxLklmIE9seW1waWFkX1BhcnRpY2lwYXRpb24gaXMgMSBwcmVkaWN0ICJBY2FkZW1pY3MiLihXaGVuIE9seW1waWFkX1BhcnRpY2lwYXRpb24gaXMgMCkgSWYgIk9seW1waWFkX1BhcnRpY2lwYXRpb24iIGlzIDAgYW5kICJGYW50X2FydHMiIGlzIDAsIHRoZW4gY2hlY2sgaWYgIkdyYXNwX3BvdyIgaXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDQgLCBwcmVkaWN0IGNsYXNzIkFydHMiLldoZW4gQ2FyZWVyX3NwcnQgaXMgMSwgdGhlbiBjaGVjayBpZiAiRmFudF9hcnRzIiBpcyAwIHRoZW4gcHJlZGljdCAiU3BvcnRzIi4gSWYgIkZhbnRfYXJ0cyIgaXMgMSwgdGhlbiBjaGVjayBpZiAiVGltZV9hcnQiIGlzIGdyZWF0ZXIgdGhhbiAyLmNoZWNrIGlmICJUaW1lX2FydCIgaXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDIsIHRoZW4gY2hlY2sgaWYgIkFjdF9zcHJ0IiBpcyAxIG9yIDAuSWYgQWN0X3NwcnQgaXMgMSwgcHJlZGljdCAiU3BvcnRzIi4gSWYgQWN0X3NwcnQgaXMgMCwgdGhlbiBjaGVjayBpZiBPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIGlzIDAgcHJlZGljdCAiQXJ0cyIuIElmICJPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIiBpcyAxLCBwcmVkaWN0ICJBY2FkZW1pY3MiLldoZW4gVGltZV9hcnQgaXMgZ3JlYXRlciB0aGFuIDIgLElmIFdvbl9hcnRzIGlzIDAsIHByZWRpY3QgY2xhc3MgIlNwb3J0cyIuDQoNCg0KKioyLUdhaW4gcmF0aW8oODAlLDIwJSkqKg0KDQpJbnN0YWxsIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJDNTAiKQ0KaW5zdGFsbC5wYWNrYWdlcygicHJpbnRyIikNCmluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQoNCmxpYnJhcnkoQzUwKQ0KbGlicmFyeShwcmludHIpDQpsaWJyYXJ5KGNhcmV0KQ0KYGBgDQoNClNldCBhIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0KDQpgYGB7cn0NCnNldC5zZWVkKDE5NTgpDQoNCmBgYA0KDQpTcGxpdHRpbmcgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdCBzZXRzDQoNCmBgYHtyfQ0KdHJhaW5faW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KEhvYmJ5X0RhdGEpLCAwLjggKiBucm93KEhvYmJ5X0RhdGEpKQ0KSG9iYnkudHJhaW4gPC0gSG9iYnlfRGF0YVt0cmFpbl9pbmRpY2VzLCBdDQpIb2JieS50ZXN0IDwtIEhvYmJ5X0RhdGFbLXRyYWluX2luZGljZXMsIF0NCg0KYGBgDQoNClRyYWluaW5nIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsDQoNCmBgYHtyfQ0KbW9kZWwgPC0gQzUuMChgUHJlZGljdGVkIEhvYmJ5YCB+IC4sIGRhdGEgPSBIb2JieS50cmFpbiwgY29udHJvbCA9IEM1LjBDb250cm9sKENGID0gMC4wMSkpDQoNCmBgYA0KDQpNYWtpbmcgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0DQoNCmBgYHtyfQ0KcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IEhvYmJ5LnRlc3QsIHR5cGUgPSAnY2xhc3MnKQ0KYGBgDQoNCkNyZWF0ZSBhIGNvbmZ1c2lvbiBtYXRyaXggZnJvbSB0aGUgcHJlZGljdGlvbnMgYW5kIGFjdHVhbCB2YWx1ZXMNCg0KYGBge3J9DQpjb25mX21hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0aW9ucywgQWN0dWFsID0gSG9iYnkudGVzdCRgUHJlZGljdGVkIEhvYmJ5YCkNCmBgYA0KDQpDYWxjdWxhdGUgYW5kIHByaW50IHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwNCg0KYGBge3J9DQphY2N1cmFjeSA8LSBzdW0oZGlhZyhjb25mX21hdHJpeCkpIC8gc3VtKGNvbmZfbWF0cml4KQ0KcHJpbnQocGFzdGUoJ0FjY3VyYWN5IG9uIHRlc3QgZGF0YSBpczonLCBhY2N1cmFjeSkpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnByaW50IHRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCg0KbWV0cmljcyA8LSBkYXRhLmZyYW1lKA0KICBDbGFzcyA9IHJvd25hbWVzKGNvbmZfbWF0cml4KSwNCiAgUHJlY2lzaW9uID0gcHJlY2lzaW9uLA0KICBSZWNhbGwgPSByZWNhbGwsDQogIFNwZWNpZmljaXR5ID0gc3BlY2lmaWNpdHkNCikNCmBgYA0KDQpQcmludCBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQobWV0cmljcykNCmBgYA0KDQpHZW5lcmF0ZSBhbmQgcHJpbnQgYWRkaXRpb25hbCBwZXJmb3JtYW5jZSBtZXRyaWNzIHVzaW5nIGNhcmV0IHBhY2thZ2UNCg0KYGBge3J9DQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnMsIEhvYmJ5LnRlc3QkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpgYGB7cn0NCnBsb3QobW9kZWwpDQpgYGANCg0KKipEZWNpc2lvbiBUcmVlIEFuYWx5c2lzIFVzaW5nIEdhaW4gUmF0aW8oODAvMjApOioqDQoNClRoZSBkZWNpc2lvbiB0cmVlIGRlcGljdGVkIGNsYXNzaWZpZXMgaG9iYmllcyBpbnRvICdBY2FkZW1pY3MnICgxKSwgJ0FydHMnICgyKSwgYW5kICdTcG9ydHMnICgzKS4gSXQgc3RhcnRzIHdpdGggJ0NhcmVlcl9zcHJ0JyBhIHZhbHVlIG9mIDAgbGVhZHMgdG8gJ1dvbl9hcnRzJy4gSWYgJ1dvbl9hcnRzJyBpcyAwIG9yIDIsIHRoZSBtb2RlbCBzdWdnZXN0cyAnQWNhZGVtaWNzJyBvciAnQXJ0cycuIElmICdDYXJlZXJfc3BydCcgaXMgMSwgJ0ZhbnRfYXJ0cycgaXMgY29uc2lkZXJlZCBuZXh0OyBhIHZhbHVlIG9mIDAgYWZ0ZXIgJ1dvbl9hcnRzJyBiZWluZyAxIHBvaW50cyB0b3dhcmRzICdBcnRzJywgd2hpbGUgYSB2YWx1ZSBvZiAxIGxlYWRzIHRvICdPbHltcGlhZF9QYXJ0aWNpcGF0aW9uJywgd2hpY2gsIGlmIDEsIGluZGljYXRlcyAnQWNhZGVtaWNzJy4gQ29udmVyc2VseSwgYSBoaWdoICdHcmFzcF9wb3cnIChcPjQpIHByZWRpY3RzICdTcG9ydHMnLg0KDQoNCg0KKiozLUdhaW4gcmF0aW8oNzAlLDMwJSkqKg0KDQpJbnN0YWxsIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJDNTAiKQ0KaW5zdGFsbC5wYWNrYWdlcygicHJpbnRyIikNCmluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQoNCmxpYnJhcnkoQzUwKQ0KbGlicmFyeShwcmludHIpDQpsaWJyYXJ5KGNhcmV0KQ0KYGBgDQoNClNldCBhIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0KDQpgYGB7cn0NCnNldC5zZWVkKDE5NTgpDQoNCmBgYA0KDQpTcGxpdHRpbmcgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdCBzZXRzDQoNCmBgYHtyfQ0KdHJhaW5faW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KEhvYmJ5X0RhdGEpLCAwLjcgKiBucm93KEhvYmJ5X0RhdGEpKQ0KSG9iYnkudHJhaW4gPC0gSG9iYnlfRGF0YVt0cmFpbl9pbmRpY2VzLCBdDQpIb2JieS50ZXN0IDwtIEhvYmJ5X0RhdGFbLXRyYWluX2luZGljZXMsIF0NCg0KYGBgDQoNClRyYWluaW5nIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsDQoNCmBgYHtyfQ0KbW9kZWwgPC0gQzUuMChgUHJlZGljdGVkIEhvYmJ5YCB+IC4sIGRhdGEgPSBIb2JieS50cmFpbiwgY29udHJvbCA9IEM1LjBDb250cm9sKENGID0gMC4wMSkpDQoNCmBgYA0KDQpNYWtpbmcgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0DQoNCmBgYHtyfQ0KcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IEhvYmJ5LnRlc3QsIHR5cGUgPSAnY2xhc3MnKQ0KYGBgDQoNCkNyZWF0ZSBhIGNvbmZ1c2lvbiBtYXRyaXggZnJvbSB0aGUgcHJlZGljdGlvbnMgYW5kIGFjdHVhbCB2YWx1ZXMNCg0KYGBge3J9DQpjb25mX21hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0aW9ucywgQWN0dWFsID0gSG9iYnkudGVzdCRgUHJlZGljdGVkIEhvYmJ5YCkNCmBgYA0KDQpDYWxjdWxhdGUgYW5kIHByaW50IHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwNCg0KYGBge3J9DQphY2N1cmFjeSA8LSBzdW0oZGlhZyhjb25mX21hdHJpeCkpIC8gc3VtKGNvbmZfbWF0cml4KQ0KcHJpbnQocGFzdGUoJ0FjY3VyYWN5IG9uIHRlc3QgZGF0YSBpczonLCBhY2N1cmFjeSkpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnByaW50IHRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCg0KbWV0cmljcyA8LSBkYXRhLmZyYW1lKA0KICBDbGFzcyA9IHJvd25hbWVzKGNvbmZfbWF0cml4KSwNCiAgUHJlY2lzaW9uID0gcHJlY2lzaW9uLA0KICBSZWNhbGwgPSByZWNhbGwsDQogIFNwZWNpZmljaXR5ID0gc3BlY2lmaWNpdHkNCikNCmBgYA0KDQpQcmludCBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQobWV0cmljcykNCmBgYA0KDQpHZW5lcmF0ZSBhbmQgcHJpbnQgYWRkaXRpb25hbCBwZXJmb3JtYW5jZSBtZXRyaWNzIHVzaW5nIGNhcmV0IHBhY2thZ2UNCg0KYGBge3J9DQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnMsIEhvYmJ5LnRlc3QkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpgYGB7cn0NCnBsb3QobW9kZWwpDQpgYGANCg0KKipEZWNpc2lvbiBUcmVlIEFuYWx5c2lzIFVzaW5nIEdhaW4gUmF0aW8oNzAlLzMwJSk6KioNCg0KSW4gVGhpcmQgVHJlZSAsd2UgZGV2aWRlIGRhdGFzZXQgaW50byB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHdpdGggc2l6ZSglNzAsJTMwKSByZXNwZWN0aXZlbHkuIEFzIHlvdSBjYW4gc2VlIGluIHRoZSBmaWd1cmUsIHRoZSByb290IG5vZGUgaXMgIkNhcmVlcl9zcHJ0IiAsIGNsYXNzIDEoIkFjYWRlbWljcyIpLCBjbGFzcyAyKCJBcnRzIiksIGFuZCBjbGFzcyAzICgiU3BvcnRzIikuDQoNClRoZSBmaXJzdCBkZWNpc2lvbiBpcyBiYXNlZCBvbiB3aGV0aGVyIHRoZSB2YWx1ZSBvZiB0aGUgIkNhcmVlcl9zcHJ0IiBhdHRyaWJ1dGUgaXMgMC5JZiAiQ2FyZWVyX3NwcnQiIGlzIDAsIHRoZW4gY2hlY2sgaWYgIldvbl9hcnRzIiBpcyBlaXRoZXIgMCBvciAyLnByZWRpY3QgIkFjYWRlbWljcyIuSWYgIldvbl9hcnRzIiBpcyAxLCB0aGVuIGNoZWNrIGlmICJGYW50X2FydHMiIGlzIDEscHJlZGljdCAiQXJ0cyIuSWYgIiJGYW50X2FydHMgaXMgMCBhbmQiV29uX2FydHMiIGlzIDAgb3IgMiwgdGhlbiBjaGVjayBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byAyLHByZWRpY3QgIkFjYWRlbWljcyIuIElmICJUaW1lX2FydCIgaXMgZ3JlYXRlciB0aGFuIDIsIHByZWRpY3QgIkFydHMiLg0KDQpJZiAiQ2FyZWVyX3NwcnQiaXMgMSwgdGhlbiBjaGVjayBpZiAiRmFudF9hcnRzIiBpcyAwLCBwcmVkaWN0ICJTcG9ydHMiLklmICJGYW50X2FydHMiaXMgMSwgdGhlbiBjaGVjayBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byAyIGNoZWNrIGlmICJBY3Rfc3BydCIgaXMgMCxwcmVkaWN0ICJBY2FkZW1pY3MiIC5JZiAiQWN0X3NwcnQiIGlzIDEgcHJlZGljdCAiU3BvcnRzIi4gaWYgIlRpbWVfYXJ0IiBpcyBncmVhdGVyIHRoYW4gMiwgdGhlbiBjaGVjayBpZiAiV29uX2FydHMiIGlzIDAgcHJlZGljdCAiU3BvcnRzIi5JZiAiV29uX2FydHMiIGlzIDEgb3IgMiBwcmVkaWN0ICJBcnRzIi4NCg0KKipDb21wYXJpbmcgRGVjaXNpb24gVHJlZSBSZXN1bHRzIFVzaW5nIEdhaW4gUmF0aW8qKg0KDQpBY3Jvc3MgVGhyZWUgVHJhaW5pbmctVGVzdCBTaXplczogVGhlIGFjY3VyYWN5IHJhdGVzIC0wLjg5NDQgZm9yIHRoZSA5MDoxMCBzcGxpdCwgMC44OTM5IGZvciB0aGUgNzA6MzAgc3BsaXQsIGFuZCAwLjg4NzkgZm9yIHRoZSA4MDoyMCBzcGxpdCAtLSBpbmRpY2F0ZSBvbmx5IHNsaWdodCB2YXJpYXRpb25zLCB3aXRoIHRoZSA5MDoxMCBzcGxpdCBiZWluZyBtYXJnaW5hbGx5IGJldHRlci4NCg0KDQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEdhaW4gcmF0aW8gICAgICB8IDkwICV0IHJhaW5pbmcgc2V0IHwgODAgJXQgcmFpbmluZyBzZXQgfCA3MCAldCByYWluaW5nIHNldCB8DQp8ICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICB8IDEwJSB0ZXN0aW5nIHNldDogIHwgMjAlIHRlc3Rpbmcgc2V0OiAgfCAzMCUgdGVzdGluZyBzZXQ6ICB8DQorOj09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09PT09Ois6PT09PT09PT09PT09PT09PT06Kzo9PT09PT09PT09PT09PT09PTorDQp8ICoqQWNjdXJhY3kqKiAgICB8IDAuODk0NCAgICAgICAgICAgIHwgMC44ODggICAgICAgICAgICAgfCAwLjg5Mzk3MDg5Mzk3MDg5NCB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqcHJlY2lzaW9uKiogICB8IDAuODkyICAgICAgICAgICAgIHwgMC44ODEgICAgICAgICAgICAgfCAwLjg5MCAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqc2Vuc2l0aXZpdHkqKiB8IDAuODkxICAgICAgICAgICAgIHwgMC44OTAgICAgICAgICAgICAgfCAwLjkwMiAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqc3BlY2lmaWNpdHkqKiB8IDAuOTQ1ICAgICAgICAgICAgIHwgMC45NDMgICAgICAgICAgICAgfCAwLjk0NyAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCg0KDQoNCg0KDQoNCg0KDQoqKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1DbHVzdGVyaW5nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSoqDQoNCg0KDQpgYGB7cn0NCnN0cihIb2JieV9wcm9jKQ0KYGBgDQoNCioqVG8gY29udmVydCB0eXBlIGZyb20gZmFjdG9yIGNvbHVtbnMgdG8gbnVtZXJpYyoqDQoNCmBgYHtyfQ0KDQpIb2JieV9wcm9jJEZhdl9zdWIgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkRmF2X3N1YikNCg0KSG9iYnlfcHJvYyRPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJE9seW1waWFkX1BhcnRpY2lwYXRpb24pDQoNCkhvYmJ5X3Byb2MkUHJvamVjdHMgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkUHJvamVjdHMpDQoNCkhvYmJ5X3Byb2MkU2Nob2xhcnNoaXAgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkU2Nob2xhcnNoaXApDQoNCkhvYmJ5X3Byb2MkQ2FyZWVyX3NwcnQgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkQ2FyZWVyX3NwcnQpDQoNCkhvYmJ5X3Byb2MkQWN0X3NwcnQgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkQWN0X3NwcnQpDQoNCkhvYmJ5X3Byb2MkRmFudF9hcnRzIDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJEZhbnRfYXJ0cykNCg0KSG9iYnlfcHJvYyRXb25fYXJ0cyA8LWFzLm51bWVyaWMoSG9iYnlfcHJvYyRXb25fYXJ0cykNCg0KSG9iYnlfcHJvYyRgUHJlZGljdGVkIEhvYmJ5YCA8LWFzLm51bWVyaWMoSG9iYnlfcHJvYyRgUHJlZGljdGVkIEhvYmJ5YCApDQpgYGANCg0KDQpgYGB7cn0NCnN0cihIb2JieV9wcm9jKQ0KYGBgDQoNCioqU2NhbGVkIEFsbCBDb2x1bW5zKioNCg0KYGBge3J9DQpIb2JieV9wcm9jIDwtc2NhbGUoSG9iYnlfcHJvYykNCmBgYA0KDQoqKkRhdGEgc2V0IHdpdGhvdXQgZ3JvdW5kIHRydXRoKioNCg0KYGBge3J9DQpIb2JieV9EYXRhMiA8LSBIb2JieV9wcm9jWywgIShjb2xuYW1lcyhIb2JieV9wcm9jKSAlaW4lIGMoIlByZWRpY3QgSG9iYnkiKSldDQpgYGANCg0KDQpPdXIgZGF0YSBzZXQgZXhoaWJpdHMgYSBiYWxhbmNlZCBkaXN0cmlidXRpb24gYW1vbmcgY2xhc3MgbGFiZWxzLCB3aXRoDQoiYWNhZGVtaWMsIiAiYXJ0LCIgYW5kICJzcG9ydCIgY29uc3RpdHV0aW5nIDQzLjYlLCAyNS42JSwgYW5kIDMwLjglIG9mDQp0aGUgdG90YWwsIHJlc3BlY3RpdmVseS4gVGhpcyBiYWxhbmNlZCBkaXN0cmlidXRpb24gaXMgYWR2YW50YWdlb3VzIGZvcg0KYm90aCBjbGFzc2lmaWNhdGlvbiBhbmQgY2x1c3RlcmluZyB0YXNrcy4gSW4gY2xhc3NpZmljYXRpb24sIGEgYmFsYW5jZWQNCmRhdGEgc2V0IGhlbHBzIHByZXZlbnQgdGhlIG1vZGVsIGZyb20gZmF2b3Jpbmcgb25lIGNsYXNzIG92ZXIgdGhlDQpvdGhlcnMsIGVuc3VyaW5nIHRoYXQgdGhlIGxlYXJuaW5nIGFsZ29yaXRobSBpcyBleHBvc2VkIHRvIGENCnJlcHJlc2VudGF0aXZlIHNldCBvZiBleGFtcGxlcyBmcm9tIGVhY2ggY2F0ZWdvcnkuIFRoaXMgYmFsYW5jZSBwcm9tb3Rlcw0KdGhlIGRldmVsb3BtZW50IG9mIGEgbW9kZWwgdGhhdCBnZW5lcmFsaXplcyB3ZWxsIGFjcm9zcyBhbGwgY2xhc3NlcywNCmVuaGFuY2luZyBpdHMgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBvbiBuZXcsIHVuc2VlbiBkYXRhLiBJbiBjbHVzdGVyaW5nLA0KYSBiYWxhbmNlZCBkYXRhIHNldCBhaWRzIGluIGZvcm1pbmcgY2x1c3RlcnMgdGhhdCBhcmUgbW9yZSBldmVubHkNCmRpc3RyaWJ1dGVkLCBhbGxvd2luZyBmb3IgYSBjb21wcmVoZW5zaXZlIHVuZGVyc3RhbmRpbmcgb2YgcGF0dGVybnMgYW5kDQpyZWxhdGlvbnNoaXBzIGFjcm9zcyBkaXZlcnNlIGNhdGVnb3JpZXMuIEJhbGFuY2VkIGRhdGEgc2V0cyBvZnRlbiBsZWFkDQp0byBtb3JlIGFjY3VyYXRlIGFuZCBmYWlyIGNsdXN0ZXJpbmcgcmVzdWx0cywgZW5hYmxpbmcgbWVhbmluZ2Z1bA0KaW5zaWdodHMgaW50byB0aGUgdW5kZXJseWluZyBzdHJ1Y3R1cmVzIHdpdGhpbiBlYWNoIGNsYXNzLg0KDQoqKnJlcXVpcmUgcGFja2FnZXMvTGlicmFyeSoqDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpIA0KaW5zdGFsbC5wYWNrYWdlcygibWFncml0dHIiKQ0KaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KbGlicmFyeShmYWN0b2V4dHJhKSANCmxpYnJhcnkoY2x1c3RlcikNCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KDQoNCioqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tay1tZWFuc2NsdXN0ZXJpbmctLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qKg0KDQoqKlZhbGlkYXRpb246KioNCg0KRGV0ZXJtaW5pbmcgdGhlIHJpZ2h0IG51bWJlciBvZiBjbHVzdGVycyBiZWZvcmUgc3RhcnRpbmcgdGhlIGNsdXN0ZXJpbmcNCnByb2Nlc3MgaXMgbGlrZSBtYWtpbmcgc3VyZSB5b3UgaGF2ZSB0aGUgY29ycmVjdC1zaXplZCBwdXp6bGUgcGllY2VzDQpiZWZvcmUgcHV0dGluZyB0aGUgcHV6emxlIHRvZ2V0aGVyLiBJdCdzIGltcG9ydGFudCBiZWNhdXNlIHNvbWUNCmNsdXN0ZXJpbmcgYWxnb3JpdGhtcywgbGlrZSBLLW1lYW5zLCByZXF1aXJlIHN1Y2ggYSBwYXJhbWV0ZXIuIEluDQphZGRpdGlvbiB0byB0aGF0LCBpdCBoZWxwcyBtYWtlIHRoZSBjbHVzdGVyaW5nIG1vcmUgYWNjdXJhdGUgYW5kIHVzZWZ1bC4NCklmIHlvdSBrbm93IHRoZSByaWdodCBudW1iZXIgYmVmb3JlaGFuZCwgaXQgc2F2ZXMgdGltZSBhbmQgaGVscHMgbWFrZQ0KdGhlIHdob2xlIHByb2Nlc3MgbW9yZSBlZmZpY2llbnQgYW5kIHRoZSByZXN1bHRzIG1vcmUgcmVsaWFibGUuDQoNCioqY29tcHV0ZSBhdmVyYWdlIHNpbGhvdWV0dGUgZm9yIGsgY2x1c3RlcnMgdXNpbmcgc2lsaG91ZXR0ZSgpIEZvciBrLW1lYW4qKg0KDQpgYGB7cn0NCnNpbGhvdWV0dGVfc2NvcmUgPC0gZnVuY3Rpb24oaykgew0KICBrbSA8LSBrbWVhbnMoSG9iYnlfRGF0YTIsIGNlbnRlcnMgPSBrLCBuc3RhcnQgPSAyNSkNCiAgc3MgPC0gc2lsaG91ZXR0ZShrbSRjbHVzdGVyLCBkaXN0KEhvYmJ5X0RhdGEyKSkNCiAgc2lsIDwtIG1lYW4oc3NbLCAzXSkNCiAgcmV0dXJuKHNpbCkNCn0NCg0KIyBrIGNsdXN0ZXIgcmFuZ2UgZnJvbSAyIHRvIDEwDQprIDwtIDI6MTANCg0KIyBjYWxsIGZ1bmN0aW9uIGZvciBlYWNoIGsgdmFsdWUNCmF2Z19zaWwgPC0gc2FwcGx5KGssIHNpbGhvdWV0dGVfc2NvcmUpDQoNCiMgcGxvdCB0aGUgcmVzdWx0cw0KcGxvdChrLCBhdmdfc2lsLCB0eXBlID0gJ2InLCB4bGFiID0gJ051bWJlciBvZiBjbHVzdGVycycsIHlsYWIgPSAnQXZlcmFnZSBTaWxob3VldHRlIFNjb3JlcycsIGZyYW1lID0gRkFMU0UpDQpgYGANCg0KVGhlIHJlc3VsdHMgaW5kaWNhdGUgdGhhdCB0aGUgaGlnaGVzdCBhdmVyYWdlIHNpbGhvdWV0dGUgc2NvcmVzIGluZGljYXRlDQp0aGUgcXVhbGl0eSBvZiB0aGUgY2x1c3RlcnMgYW5kIHN1Z2dlc3QgYmV0dGVyLWRlZmluZWQgYW5kIG1vcmUNCnNlcGFyYXRlZCBjbHVzdGVycywgd2l0aCBlYWNoIHBvaW50IGhhdmluZyBhIGhpZ2ggZGVncmVlIG9mIHNpbWlsYXJpdHkNCnRvIGl0cyBvd24gY2x1c3RlciBhbmQgYSBsb3dlciBzaW1pbGFyaXR5IHRvIG5laWdoYm9yaW5nIGNsdXN0ZXJzLiBUaGUNCmhpZ2hlc3QgYXZlcmFnZSBzaWxob3VldHRlIHNjb3JlcyB3ZXJlIG9ic2VydmVkIGZvciBrIHZhbHVlcyBvZiAzLCAyLA0KYW5kIDQsIHNvIHRoZXNlIGFyZSB0aGUgb3B0aW1hbCBudW1iZXJzLiBUaGVzZSB2YWx1ZXMgd2lsbCBiZSBlbXBsb3llZA0KaW4gc3Vic2VxdWVudCBrLW1lYW5zIGNsdXN0ZXJpbmcgYW5hbHlzZXMuDQoNCioqay1tZWFucyBjbHVzdGVyIGs9MyoqDQoNCmBgYHtyfQ0KI3NldCBhIHNlZWQgZm9yIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiAgdG8gbWFrZSB0aGUgcmVzdWx0cyByZXByb2R1Y2libGUNCnNldC5zZWVkKDcpDQprbWVhbnMucmVzdWx0IDwtIGttZWFucyhIb2JieV9EYXRhMiwgMykNCg0KIyBwcmludCB0aGUgY2x1c3Rlcm5nIHJlc3VsdA0Ka21lYW5zLnJlc3VsdA0KDQojdmlzdWFsaXplIGNsdXN0ZXJpbmcNCmZ2aXpfY2x1c3RlcihrbWVhbnMucmVzdWx0LCBkYXRhID0gSG9iYnlfRGF0YTIpDQpgYGANCg0KKipBdmVyYWdlIGZvciBlYWNoIGNsdXN0ZXIqKg0KDQpgYGB7cn0NCmF2Z19zaWwgPC0gc2lsaG91ZXR0ZShrbWVhbnMucmVzdWx0JGNsdXN0ZXIsZGlzdChIb2JieV9EYXRhMikpIA0KZnZpel9zaWxob3VldHRlKGF2Z19zaWwpDQpgYGANCg0KdGhlIHByZXNlbmNlIG9mIG5lZ2F0aXZlIHNpbGhvdWV0dGVzIGZvciBzb21lIG9ic2VydmF0aW9ucyB3aXRoaW4gZWFjaA0KY2x1c3RlciBpbmRpY2F0ZXMgdGhhdCB0aGVzZSBwb2ludHMgbWlnaHQgYmUgbW9yZSBzaW1pbGFyIHRvIHBvaW50cyBpbg0Kb3RoZXIgY2x1c3RlcnMsIHN1Z2dlc3RpbmcgYSBwb3RlbnRpYWwgb3ZlcmxhcCBvciBhbWJpZ3VpdHkgaW4gdGhlaXINCmFzc2lnbm1lbnQuIFRoZSBmYWN0IHRoYXQgc29tZSBvYnNlcnZhdGlvbnMgaGF2ZSBuZWdhdGl2ZSBzaWxob3VldHRlcw0KaGlnaGxpZ2h0cyB0aGF0IHRoZSBzZXBhcmF0aW9uIG9mIGRhdGEgcG9pbnRzIGlzIG5vdCBlbnRpcmVseQ0Kc3VmZmljaWVudC4NCg0KU28gc2luY2UgMywgd2hpY2ggd2FzIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyB3aXRoIHRoZSBoaWdoZXN0DQpzaWxob3VldHRlIHNjb3JlIGF2ZXJhZ2UsIGRpZCBub3QgaGF2ZSBnb29kIGNsdXN0ZXJpbmcgcmVzdWx0cywgdGhhdA0KZG9lcyBzdXBwb3J0IG91ciByZXNlYXJjaCByZXN1bHRzIHRoYXQgdGhlIGstbWVhbnMgYWxnb3JpdGhtIGlzIG5vdA0KYXBwbGljYWJsZSB0byBjYXRlZ29yaWNhbGRhdGEgY2x1c3RlcmluZyBiZWNhdXNlIGl0IHJlbGllcyBvbiB0aGUNCkV1Y2xpZGVhbiBkaXN0YW5jZSBtZXRyaWMgdG9tZWFzdXJlIHRoZSBzaW1pbGFyaXR5IGJldHdlZW4gZGF0YSBwb2ludHMuDQpIb3dldmVyLCBldmVuIGFmdGVyRW5jb2RpbmcgYW5kIGl0cyBhcHBsaWNhdGlvbiB0byBjYXRlZ29yaWNhbCBkYXRhIHBvc2UNCnNpZ25pZmljYW50IGNoYWxsZW5nZXMuIENhdGVnb3JpY2FsIHZhcmlhYmxlcyBvZnRlbiBsYWNrIGEgbWVhbmluZ2Z1bA0KbnVtZXJpY2FscmVwcmVzZW50YXRpb247IGZvciBpbnN0YW5jZSwgdGFraW5nIHRoZSBtZWFuIG9mIGNhdGVnb3JpZXMNCmxpa2UgdGhlZmVhdHVyZSAiZmF2b3JpdGUgc3ViamVjdCwiIGFrYSBGYXZfc3ViIiAoZXZlbiBhZnRlciBlbmNvZGluZyksDQptaWdodCBub3RoYXZlIGFueSBwcmFjdGljYWwgaW50ZXJwcmV0YXRpb24uIEFuZCB0aGUgZGlzdGFuY2VzIGNhbGN1bGF0ZWQNCmluIHRoZWFsZ29yaXRobSBtYXkgbm90IHJlZmxlY3QgdGhlIHRydWUgZGlzc2ltaWxhcml0aWVzIGJldHdlZW4NCmNhdGVnb3JpY2FsdmFsdWVzLiBUaGUgZW5jb2RpbmcgcHJvY2VzcyBpdHNlbGYgaW50cm9kdWNlcyBhcnRpZmljaWFsDQpudW1lcmljYWxyZWxhdGlvbnNoaXBzIHRoYXQgbWF5IG1pc2xlYWQgdGhlIGFsZ29yaXRobS4gTW9yZW92ZXIsIGstbWVhbnMNCnJlbGllc29uIHRoZSBtaW5pbWl6YXRpb24gb2YgRXVjbGlkZWFuIGRpc3RhbmNlcywgd2hpY2ggbWlnaHQgbm90DQphY2N1cmF0ZWx5Y2FwdHVyZSB0aGUgZGlzc2ltaWxhcml0eSBzdHJ1Y3R1cmUgaW4gY2F0ZWdvcmljYWwgZGF0YS4NCkNhdGVnb3JpY2FsdmFyaWFibGVzIGluaGVyZW50bHkgZXhoaWJpdCBkaXNjcmV0ZSBhbmQgbm9uLW9yZGluYWwNCmNoYXJhY3RlcmlzdGljc3RoYXQgYXJlIG5vdCB3ZWxsLXN1aXRlZCBmb3IgdGhlIGNvbnRpbnVvdXMgYW5kIGxpbmVhcg0KYXNzdW1wdGlvbnMgb2ZrLW1lYW5zLiBBbHRlcm5hdGl2ZSBjbHVzdGVyaW5nIHRlY2huaXF1ZXMsIHNwZWNpZmljYWxseQ0KZGVzaWduZWQgZm9yY2F0ZWdvcmljYWwgZGF0YSwgc3VjaCBhcyBwYXJ0aXRpb25pbmcgYXJvdW5kIG1lZG9pZHMsIGFyZQ0KbW9yZWFwcHJvcHJpYXRlIGZvciBjYXB0dXJpbmcgdGhlIGludHJpbnNpYyBwYXR0ZXJucyBhbmQgcmVsYXRpb25zaGlwcw0KaW5jYXRlZ29yaWNhbCBkYXRhc2V0cy4NCg0KDQoNCg0KDQoNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLWstbWVkaW9kcyBjbHVzdGVyaW5nIHdpdGggUEFNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qKg0KDQpLLW1lZG9pZHMgY2x1c3RlcmluZyBwcmVzZW50cyBhIHJvYnVzdCBhbHRlcm5hdGl2ZSBmb3IgYW5hbHl6aW5nDQpjYXRlZ29yaWNhbCBkYXRhIGJ5IGFkZHJlc3NpbmcgdGhlIGxpbWl0YXRpb25zIHBvc2VkIGJ5IGstbWVhbnMNCmNsdXN0ZXJpbmcuIFVubGlrZSBrLW1lYW5zLCBrLW1lZG9pZHMgZG9lcyBub3QgcmVseSBvbiB0aGUgbWVhbiBhcyBhDQpyZXByZXNlbnRhdGl2ZSBjZW50cm9pZCBidXQgZW1wbG95cyBtZWRvaWRzLCB3aGljaCBhcmUgYWN0dWFsIGRhdGENCnBvaW50cyB3aXRoaW4gdGhlIGNsdXN0ZXJzLCBhbmQgdGhlIGFsZ29yaXRobSBkZWZpbmVzIGNsdXN0ZXJzIGJhc2VkIG9uDQpwYXJ0aXRpb25pbmcgYXJvdW5kIG1lZG9pZHMuIFRoaXMgZmVhdHVyZSBtYWtlcyBrLW1lZG9pZHMgcGFydGljdWxhcmx5DQpzdWl0YWJsZSBmb3IgY2F0ZWdvcmljYWwgZGF0YSwgd2hlcmUgbWVhbmluZ2Z1bCBjZW50cm9pZHMgbWF5IG5vdCBoYXZlIGENCm51bWVyaWNhbCBpbnRlcnByZXRhdGlvbi4gVGhlIGFsZ29yaXRobSBkZWZpbmVzIGNsdXN0ZXJzIGJhc2VkIG9uDQpwYXJ0aXRpb25pbmcgYXJvdW5kIG1lZG9pZHMuDQoNCioqVmFsaWRhdGlvbjoqKg0KDQpGaXJzdCwgd2Ugd2FudCB0byBkZXRlcm1pbmUgdGhyZWUgZGlmZmVyZW50IG51bWJlcnMgb2YgY2x1c3RlcnMgYnkgdXNpbmcNCmEgbnVtYmVyIG9mIG1ldGhvZHMgdGhhdCB3aWxsIHN1Z2dlc3QgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGZvcg0Kay1tZWRpb2RzIGNsdXN0ZXJpbmcgd2l0aCBQQU0uDQoNCioqU2lsaG91ZXR0ZSBjb2VmZmljaWVudCoqDQoNCmBgYHtyfQ0KZnZpel9uYmNsdXN0KEhvYmJ5X0RhdGEyLCBwYW0sIG1ldGhvZCA9ICJzaWxob3VldHRlIikrDQogIGxhYnMoc3VidGl0bGUgPSAiU2lsaG91ZXR0ZSBtZXRob2QiKQ0KYGBgDQoNCioqRWxib3cgbWV0aG9kKioNCg0KYGBge3J9DQpmdml6X25iY2x1c3QoSG9iYnlfRGF0YTIsIHBhbSwgbWV0aG9kID0gIndzcyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PSAzLCBsaW5ldHlwZT0gMykrDQogIGxhYnMoc3VidGl0bGUgPSAiRWxib3cgbWV0aG9kIikNCmBgYA0KDQpUaGUgc2lsaG91ZXR0ZSBjb2VmZmljaWVudCwgd2hpY2ggbWVhc3VyZXMgdGhlIGNvaGVzaW9uIGFuZCBzZXBhcmF0aW9uDQpvZiBjbHVzdGVycywgYWxpZ25zIHdpdGggdGhlIEVsYm93IG1ldGhvZCwgd2hpY2ggYXNzZXNzZXMgdGhlDQp3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcyAod3NzKSBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBudW1iZXIgb2YNCmNsdXN0ZXJzIGluIHRoZSBzdWdnZXN0ZWQgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMuIFRoaXMgYWxpZ25tZW50IGluDQpyZXN1bHRzIGJldHdlZW4gdHdvIGRpc3RpbmN0IGV2YWx1YXRpb24gbWV0aG9kcyBzdHJlbmd0aGVucyBjb25maWRlbmNlDQppbiB0aGUgY2hvaWNlIG9mIHRocmVlIGNsdXN0ZXJzLCBwcm92aWRpbmcgYSBzdGFibGUgZm91bmRhdGlvbiBmb3INCmZ1cnRoZXIgYW5hbHlzaXMgYW5kIGludGVycHJldGF0aW9uIG9mIHRoZSB1bmRlcmx5aW5nIHBhdHRlcm5zIHdpdGhpbg0KdGhlIGRhdGFzZXQuIEluIGFkZGl0aW9uIHRvIHRoYXQsIHRoZSBncm91bmQgdHJ1dGggKGNsYXNzIGxhYmVscykgYWxzbw0KY29udGFpbnMgdGhyZWUgY2xhc3NlcywgYW5kIHRoYXQgaW5kaWNhdGVzIGEgcmVhc3N1cmluZyBhbGlnbm1lbnQNCmJldHdlZW4gdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBhbmQgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cy4NCg0KDQoNCldlIG5lZWQgdG8gZGV0ZXJtaW5lIHR3byBtb3JlIHN1Z2dlc3RlZCBudW1iZXJzIG9mIGNsdXN0ZXJzIGJ5IGNvbXB1dGluZw0KdGhlIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSBmb3IgayBjbHVzdGVycyB1c2luZyBzaWxob3VldHRlKCkuDQpgYGB7cn0NCnNpbGhvdWV0dGVfc2NvcmUgPC0gZnVuY3Rpb24oaykgew0KICBrbSA8LSBwYW0oSG9iYnlfRGF0YTIsIGssIGRpc3MgPSBUUlVFKQ0KICBzcyA8LSBzaWxob3VldHRlKGttJGNsdXN0ZXJpbmcsIGRpc3QoSG9iYnlfRGF0YTIpKQ0KICBzaWwgPC0gbWVhbihzc1ssIDNdKQ0KICByZXR1cm4oc2lsKQ0KfQ0KDQojIGsgY2x1c3RlciByYW5nZSBmcm9tIDIgdG8gMTANCmsgPC0gMjoxMA0KDQojIENhbGwgZnVuY3Rpb24gZm9yIGVhY2ggayB2YWx1ZQ0KYXZnX3NpbCA8LSBzYXBwbHkoaywgc2lsaG91ZXR0ZV9zY29yZSkNCg0KIyBQbG90IHRoZSByZXN1bHRzDQpwbG90KGssIGF2Z19zaWwsIHR5cGUgPSAnYicsIHhsYWIgPSAnTnVtYmVyIG9mIGNsdXN0ZXJzJywgeWxhYiA9ICdBdmVyYWdlIFNpbGhvdWV0dGUgU2NvcmVzJywgZnJhbWUgPSBGQUxTRSkNCmBgYA0KDQpJdCBpcyBhIGNvbW1vbiBwcmFjdGljZSB0byBjaG9vc2UgdGhlIG51bWJlciBvZiBjbHVzdGVycyBjb3JyZXNwb25kaW5nDQp0byB0aGUgcGVhayBpbiB0aGUgc2lsaG91ZXR0ZSBzY29yZSBwbG90LCBhbmQgc2luY2Ugd2UgYXJlIGxvb2tpbmcgZm9yDQp0d28gbW9yZSBudW1iZXIgb2YgY2x1c3RlcnMgb3RoZXIgdGhhbiB0aHJlZSwgaXQgd291bGQgYmUgcmVhc29uYWJsZSB0bw0KY29uc2lkZXIgdHdvIGFuZCBmb3VyIGNsdXN0ZXJzIGZvciBmdXJ0aGVyIGFuYWx5c2lzLiBFc3BlY2lhbGx5IHdpdGggdGhlDQpkZWNyZWFzaW5nIHRyZW5kIGJleW9uZCB0aHJlZSBjbHVzdGVycywgaXQgaW5kaWNhdGVzIHRoYXQgYWRkaW5nIG1vcmUNCmNsdXN0ZXJzIGRvZXMgbm90IHNpZ25pZmljYW50bHkgaW1wcm92ZSB0aGUgc2VwYXJhdGlvbiBhbmQgY29oZXNpb24gb2YNCnRoZSBjbHVzdGVycy4NCg0KDQoNCioqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1ncm91cCBpbnRvIGs9MyBjbHVzdGVycy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KVGhlIHN1Yi1zYW1wbGluZyBhbmQgY2x1c3RlcmluZyBhcHByb2FjaCBpcyBhIGhlbHBmdWwgbWV0aG9kIHRvIGV2YWx1YXRlDQp0aGUgcm9idXN0bmVzcyBvZiB0aGUgY2x1c3RlcmluZyByZXN1bHRzIHVuZGVyIHZhcmlvdXMgc3Vic2V0cyBhbmQgdG8NCm9idGFpbiBpbnNpZ2h0cyBpbnRvIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEuIEZ1cnRoZXJtb3JlLCB3ZSB3aWxsDQp0YWtlIDEwMCBzYW1wbGVzIGZyb20gb3VyIGRhdGEgc2V0IHRvIGVuc3VyZSB0aGF0IHRoZSBjbHVzdGVyaW5nIHBsb3QNCmRvZXNuJ3QgZ2V0IHRvbyBjcm93ZGVkLg0KDQpgYGB7cn0NCnNldC5zZWVkKDcpDQoNCiMgU3BlY2lmeSB0aGUgbnVtYmVyIG9mIHJvd3MgeW91IHdhbnQgdG8gc2FtcGxlDQpudW1fcm93cyA8LSAxMDANCg0KIyBVc2Ugc2FtcGxlIHdpdGggdGhlIHNwZWNpZmllZCBzZWVkDQppZHggPC0gc2FtcGxlKDE6ZGltKEhvYmJ5X0RhdGEyKVsxXSwgbnVtX3Jvd3MpDQoNCkhvYmJ5X0RhdGEzIDwtIEhvYmJ5X0RhdGEyW2lkeCwgXQ0KDQpwYW0ucmVzdWx0IDwtIHBhbShIb2JieV9EYXRhMywzKQ0KI1Nob3cgdGhlIHNpbGhvdXRlZSBwbG90IG9mIFBBTSBBTkQgY2x1c3RlcnMNCnBsb3QocGFtLnJlc3VsdCkNCmBgYA0KDQpgYGB7cn0NCiMgRXh0cmFjdCB0aGUgY2x1c2luZm8gY29tcG9uZW50DQpjbHVzaW5mbyA8LSBwYW0ucmVzdWx0JGNsdXNpbmZvDQoNCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcw0KdG90X3dpdGhpbnNzIDwtIHN1bShjbHVzaW5mb1ssICJzaXplIl0gKiBjbHVzaW5mb1ssICJhdl9kaXNzIl1eMikNCg0KIyBQcmludCB0aGUgcmVzdWx0DQpwcmludCh0b3Rfd2l0aGluc3MpDQpgYGANCg0KVGhlIG91dHB1dCBvZiB0aGUgY29kZSwgZGlzcGxheWluZyBhIHNpbGhvdWV0dGUgcGxvdCBvZiB0aGUgUEFNDQpjbHVzdGVycywgaW5kaWNhdGVzIHRoYXQgdGhlIGNsdXN0ZXJzIGFyZSByZWxhdGl2ZWx5IGNsb3NlIHRvIGVhY2gNCm90aGVyLCB3aXRoIGEgc2xpZ2h0IG92ZXJsYXAgYmV0d2VlbiB0d28gY2x1c3RlcnMuIFRoZSBzaWxob3VldHRlIHBsb3QNCnZpc3VhbGx5IHJlcHJlc2VudHMgaG93IHdlbGwtZGVmaW5lZCBhbmQgc2VwYXJhdGVkIHRoZSBjbHVzdGVycyBhcmUuDQpXaGlsZSB0aGUgc2xpZ2h0IG92ZXJsYXAgc3VnZ2VzdHMgdGhhdCB0aGUgbmF0dXJhbCBncm91cGluZyB3aXRoaW4gdGhlDQpkYXRhc2V0IG1heSBub3QgYmUgZW50aXJlbHkgZGlzdGluY3QsIGl0IGRvZXMgbm90IG5lZ2F0aXZlbHkgYWZmZWN0IHRoZQ0Kb3ZlcmFsbCBxdWFsaXR5IG9mIHRoZSBjbHVzdGVycy4gSW4gZmFjdCwgdGhlIG9ic2VydmVkIG92ZXJsYXAgbWlnaHQNCmluZGljYXRlIHNoYXJlZCBjaGFyYWN0ZXJpc3RpY3MgYmV0d2VlbiBhZGphY2VudCBjbHVzdGVycywgZWZmZWN0aXZlbHkNCmNhcHR1cmluZyBtZWFuaW5nZnVsIHBhdHRlcm5zIGFuZCBncm91cGluZ3MgdGhhdCByZWZsZWN0IHRoZSBpbnRyaWNhY2llcw0Kb2YgcmVhbC13b3JsZCBwaGVub21lbmEgbm90IGNvbmZpbmVkIHRvIHN0cmljdCBib3VuZGFyaWVzLg0KDQpiQ3ViZWQNCg0KYGBge3J9DQpjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIGMocGFtLnJlc3VsdCRjbHVzdGVyKQ0Kc2V0LnNlZWQoNykNCg0KIyBTcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB5b3Ugd2FudCB0byBzYW1wbGUNCm51bV9yb3dzIDwtIDEwMA0KDQojIFVzZSBzYW1wbGUgd2l0aCB0aGUgc3BlY2lmaWVkIHNlZWQNCmlkeCA8LSBzYW1wbGUoMTpkaW0oSG9iYnlfcHJvYylbMV0sIG51bV9yb3dzKQ0KDQojIFNlbGVjdCB0aGUgc2FtcGxlZCByb3dzIGZyb20gSG9iYnlfcHJvYw0KSG9iYnlfRGF0YTQgPC0gSG9iYnlfcHJvY1tpZHgsIF0NCmdyb3VuZF90cnV0aF9sYWJlbHMgPC0gYyhIb2JieV9EYXRhNCkNCg0KIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggY2x1c3RlciBhc3NpZ25tZW50cyBhbmQgZ3JvdW5kIHRydXRoIGxhYmVscw0KZGF0YXNldCA8LSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBjbHVzdGVyX2Fzc2lnbm1lbnRzLCBsYWJlbCA9IGdyb3VuZF90cnV0aF9sYWJlbHMpDQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzIDwtIGZ1bmN0aW9uKGRhdGFzZXQpIHsNCiAgbiA8LSBucm93KGRhdGFzZXQpDQogIHByZWNpc2lvbl9zdW0gPC0gMCANCiAgcmVjYWxsX3N1bSA8LSAwDQogIA0KICAgZm9yIChpIGluIDE6bikgew0KICAgIGNsdXN0ZXIgPC0gZGF0YXNldCRjbHVzdGVyW2ldIA0KICAgIGxhYmVsIDwtIGRhdGFzZXQkbGFiZWxbaV0NCiAgICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgZnJvbSB0aGUgc2FtZSBjYXRlZ29yeSBpbiBpdHMgY2x1c3Rlcg0KICAgIHNhbWVfY2F0ZWdvcnkgPC0gc3VtKGRhdGFzZXQkbGFiZWxbZGF0YXNldCRjbHVzdGVyID09IGNsdXN0ZXJdID09IGxhYmVsKSAgIA0KICAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2x1c3RlciAgICANCiAgICBzYW1lX2NsdXN0ZXIgPC0gc3VtKGRhdGFzZXQkY2x1c3RlciA9PSBjbHVzdGVyKQ0KICAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2F0ZWdvcnkNCiAgICB0b3RhbF9zYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsID09IGxhYmVsKSAgIA0KICAgIA0KICAgICMgQ2FsY3VsYXRlIHByZWNpc2lvbiBhbmQgcmVjYWxsIA0KICAgIHByZWNpc2lvbl9zdW0gPC0gcHJlY2lzaW9uX3N1bSArIHNhbWVfY2F0ZWdvcnkgLyBzYW1lX2NsdXN0ZXINCiAgICByZWNhbGxfc3VtIDwtIHJlY2FsbF9zdW0gKyBzYW1lX2NhdGVnb3J5IC8gdG90YWxfc2FtZV9jYXRlZ29yeSANCiAgICB9DQogICMgRW5kIGxvb3AgDQogIA0KICAjIENhbGN1bGF0ZSBhdmVyYWdlIHByZWNpc2lvbiBhbmQgcmVjYWxsICANCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbl9zdW0gLyBuDQogIHJlY2FsbCA8LSByZWNhbGxfc3VtIC8gbiANCiAgcmV0dXJuKGxpc3QocHJlY2lzaW9uID0gcHJlY2lzaW9uLCByZWNhbGwgPSByZWNhbGwpKX0NCg0KICAjIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwNCiAgbWV0cmljcyA8LSBjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MoZGF0YXNldCkNCiAgcHJlY2lzaW9uIDwtIG1ldHJpY3MkcHJlY2lzaW9uDQogIHJlY2FsbCA8LSBtZXRyaWNzJHJlY2FsbA0KDQojIFByaW50IHRoZSByZXN1bHRzDQogIGNhdCgiQkN1YmVkIFByZWNpc2lvbj0gIiwgcHJlY2lzaW9uLCAiQU5EIEJDdWJlZCBSZWNhbGw9ICIsIHJlY2FsbCwgIlxuIikNCmBgYA0KDQpXaGlsZSBwcmVjaXNpb24gaGlnaGxpZ2h0cyByb29tIGZvciBiZXR0ZXIgYWNjdXJhY3kgaW4gaWRlbnRpZnlpbmcNCnNpbWlsYXIgaXRlbXMsIHRoZSBoaWdoZXIgcmVjYWxsIGluZGljYXRlcyB0aGUgYWxnb3JpdGhtJ3MgY2FwYWJpbGl0eSB0bw0KY2F0Y2ggYSBnb29kIGFtb3VudCBvZiBhY3R1YWwgc2ltaWxhcml0aWVzIHdpdGhpbiBjbHVzdGVycy4NCg0KDQoNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLWdyb3VwIGludG8gaz00IGNsdXN0ZXJzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KYGBge3J9DQpzZXQuc2VlZCg3KQ0KDQojIFNwZWNpZnkgdGhlIG51bWJlciBvZiByb3dzIHlvdSB3YW50IHRvIHNhbXBsZQ0KbnVtX3Jvd3MgPC0gMTAwDQoNCiMgVXNlIHNhbXBsZSB3aXRoIHRoZSBzcGVjaWZpZWQgc2VlZA0KaWR4IDwtIHNhbXBsZSgxOmRpbShIb2JieV9EYXRhMilbMV0sIG51bV9yb3dzKQ0KDQpIb2JieV9EYXRhMyA8LSBIb2JieV9EYXRhMltpZHgsIF0NCg0KcGFtLnJlc3VsdCA8LSBwYW0oSG9iYnlfRGF0YTMsNCkNCiNTaG93IHRoZSBzaWxob3V0ZWUgcGxvdCBvZiBQQU0gQU5EIGNsdXN0ZXJzDQpwbG90KHBhbS5yZXN1bHQpDQpgYGANCg0KDQpgYGB7cn0NCiMgRXh0cmFjdCB0aGUgY2x1c2luZm8gY29tcG9uZW50DQpjbHVzaW5mbyA8LSBwYW0ucmVzdWx0JGNsdXNpbmZvDQoNCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcw0KdG90X3dpdGhpbnNzIDwtIHN1bShjbHVzaW5mb1ssICJzaXplIl0gKiBjbHVzaW5mb1ssICJhdl9kaXNzIl1eMikNCg0KIyBQcmludCB0aGUgcmVzdWx0DQpwcmludCh0b3Rfd2l0aGluc3MpDQpgYGANClRoZSBvdXRwdXQgc3VnZ2VzdHMgdGhhdCB0aGUgZGF0YXNldCBtYXkgZXhoaWJpdCBhIGRlZ3JlZSBvZiBvdmVybGFwIG9yDQpzaW1pbGFyaXR5IGFtb25nIG9ic2VydmF0aW9ucy4gVGhlIG92ZXJsYXBwaW5nIGNsdXN0ZXJzIG1heSBpbmRpY2F0ZQ0KY2hhbGxlbmdlcyBpbiBhY2hpZXZpbmcgYSBjbGVhciBzZXBhcmF0aW9uIGFtb25nIHRoZXNlIGdyb3Vwcy4gVGhlDQpwbGFjZW1lbnQgb2Ygb25lIGNsdXN0ZXIgb24gdG9wIG9mIHR3byBvdGhlcnMgaW1wbGllcyB0aGF0IHRoZSBtZWRvaWQgb2YNCnRoaXMgY2x1c3RlciBtaWdodCBiZSBuZWFyIHBvaW50cyBiZWxvbmdpbmcgdG8gdGhvc2UgdHdvIG5laWdoYm9yaW5nDQpjbHVzdGVycy4gVGhpcyBjb3VsZCBiZSBkdWUgdG8gdGhlIG5hdHVyZSBvZiB0aGUgZGF0YS4NCg0KYGBge3J9DQpjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIHBhbS5yZXN1bHQkY2x1c3RlcmluZw0KDQojIEFkZCBjbHVzdGVyIGFzc2lnbm1lbnRzIHRvIHRoZSBvcmlnaW5hbCBkYXRhDQpIb2JieV9EYXRhX3dpdGhfY2x1c3RlcnMgPC0gY2JpbmQoSG9iYnlfRGF0YTMsIENsdXN0ZXIgPSBmYWN0b3IoY2x1c3Rlcl9hc3NpZ25tZW50cykpDQoNCiMgRXhhbXBsZTogUGFpciBwbG90IGZvciB0aGUgZmlyc3QgZm91ciBmZWF0dXJlcw0KcGFpcnMoSG9iYnlfRGF0YV93aXRoX2NsdXN0ZXJzWywgMTo0XSwgY29sID0gY2x1c3Rlcl9hc3NpZ25tZW50cykNCg0KDQoNCmBgYA0KDQoNCg0KDQoNCkJDdWJlZA0KYGBge3J9DQpjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIGMocGFtLnJlc3VsdCRjbHVzdGVyKSANCiANCnNldC5zZWVkKDcpIA0KIA0KIyBTcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB5b3Ugd2FudCB0byBzYW1wbGUgDQpudW1fcm93cyA8LSAxMDAgDQogDQojIFVzZSBzYW1wbGUgd2l0aCB0aGUgc3BlY2lmaWVkIHNlZWQgDQppZHggPC0gc2FtcGxlKDE6ZGltKEhvYmJ5X3Byb2MpWzFdLCBudW1fcm93cykgDQogDQojIFNlbGVjdCB0aGUgc2FtcGxlZCByb3dzIGZyb20gSG9iYnlfcHJvYyANCkhvYmJ5X0RhdGE0IDwtIEhvYmJ5X3Byb2NbaWR4LCBdIA0KDQojIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBjbHVzdGVyIGFzc2lnbm1lbnRzIGFuZCBncm91bmQgdHJ1dGggbGFiZWxzDQpkYXRhc2V0IDwtIGRhdGEuZnJhbWUoY2x1c3RlciA9IGNsdXN0ZXJfYXNzaWdubWVudHMsIGxhYmVsID0gZ3JvdW5kX3RydXRoX2xhYmVscykNCg0KIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsDQpjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MgPC0gZnVuY3Rpb24oZGF0YXNldCkgew0KICBuIDwtIG5yb3coZGF0YXNldCkNCiAgcHJlY2lzaW9uX3N1bSA8LSAwDQogIHJlY2FsbF9zdW0gPC0gMA0KIA0KICBmb3IgKGkgaW4gMTpuKSB7DQogICAgY2x1c3RlciA8LSBkYXRhc2V0JGNsdXN0ZXJbaV0NCiAgICBsYWJlbCA8LSBkYXRhc2V0JGxhYmVsW2ldDQogICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgZnJvbSB0aGUgc2FtZSBjYXRlZ29yeSBpbiBpdHMgY2x1c3Rlcg0KICAgIHNhbWVfY2F0ZWdvcnkgPC0gc3VtKGRhdGFzZXQkbGFiZWxbZGF0YXNldCRjbHVzdGVyID09IGNsdXN0ZXJdID09IGxhYmVsKQ0KICAgDQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGluIGl0cyBjbHVzdGVyDQogICAgc2FtZV9jbHVzdGVyIDwtIHN1bShkYXRhc2V0JGNsdXN0ZXIgPT0gY2x1c3RlcikNCiAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2F0ZWdvcnkNCiAgICB0b3RhbF9zYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsID09IGxhYmVsKQ0KICAgDQogICAgIyBDYWxjdWxhdGUgcHJlY2lzaW9uIGFuZCByZWNhbGwNCiAgICBwcmVjaXNpb25fc3VtIDwtIHByZWNpc2lvbl9zdW0gKyBzYW1lX2NhdGVnb3J5IC8gc2FtZV9jbHVzdGVyDQogICAgcmVjYWxsX3N1bSA8LSByZWNhbGxfc3VtICsgc2FtZV9jYXRlZ29yeSAvIHRvdGFsX3NhbWVfY2F0ZWdvcnkNCiAgfQ0KICAjIEVuZCBsb29wDQogDQogICMgQ2FsY3VsYXRlIGF2ZXJhZ2UgcHJlY2lzaW9uIGFuZCByZWNhbGwNCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbl9zdW0gLyBuDQogIHJlY2FsbCA8LSByZWNhbGxfc3VtIC8gbg0KIA0KICByZXR1cm4obGlzdChwcmVjaXNpb24gPSBwcmVjaXNpb24sIHJlY2FsbCA9IHJlY2FsbCkpDQp9DQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KbWV0cmljcyA8LSBjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MoZGF0YXNldCkNCnByZWNpc2lvbiA8LSBtZXRyaWNzJHByZWNpc2lvbg0KcmVjYWxsIDwtIG1ldHJpY3MkcmVjYWxsDQoNCiMgUHJpbnQgdGhlIHJlc3VsdHMNCmNhdCgiQkN1YmVkIFByZWNpc2lvbj0gIiwgcHJlY2lzaW9uLCAiQU5EIEJDdWJlZCBSZWNhbGw9ICIsIHJlY2FsbCwgIlxuIikNCmBgYA0KDQpUaGUgcmVzdWx0cyBpbmRpY2F0ZSBjaGFsbGVuZ2VzIGluIGNsdXN0ZXJpbmcgcGVyZm9ybWFuY2UuIFRoZSBsb3cNCnByZWNpc2lvbiBzdWdnZXN0cyBhIHNpZ25pZmljYW50IHJhdGUgb2YgbWlzY2xhc3NpZmljYXRpb24sIHdoaWxlIHRoZQ0KcmVsYXRpdmVseSBsb3cgcmVjYWxsIGluZGljYXRlcyB0aGF0IHNvbWUgaW5zdGFuY2VzIHdpdGhpbiB0aGUgc2FtZQ0KZ3JvdXAgYXJlIG1pc3NlZCBvciBpbmNvcnJlY3RseSBhc3NpZ25lZCB0byBvdGhlciBjbHVzdGVycy4gVGhlc2UNCnJlc3VsdHMgaGlnaGxpZ2h0IGxpbWl0YXRpb25zIGluIGFjY3VyYXRlbHkgY2FwdHVyaW5nIHRoZSBkYXRhJ3MNCnVuZGVybHlpbmcgc3RydWN0dXJlLg0KDQoNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tZ3JvdXAgaW50byBrPTIgY2x1c3RlcnMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSoqDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNykNCg0KIyBTcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB5b3Ugd2FudCB0byBzYW1wbGUNCm51bV9yb3dzIDwtIDEwMA0KDQojIFVzZSBzYW1wbGUgd2l0aCB0aGUgc3BlY2lmaWVkIHNlZWQNCmlkeCA8LSBzYW1wbGUoMTpkaW0oSG9iYnlfRGF0YTIpWzFdLCBudW1fcm93cykNCg0KSG9iYnlfRGF0YTMgPC0gSG9iYnlfRGF0YTJbaWR4LCBdDQoNCnBhbS5yZXN1bHQgPC0gcGFtKEhvYmJ5X0RhdGEzLDIpDQojU2hvdyB0aGUgc2lsaG91dGVlIHBsb3Qgb2YgUEFNIEFORCBjbHVzdGVycw0KcGxvdChwYW0ucmVzdWx0KQ0KYGBgDQoNCg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIGNsdXNpbmZvIGNvbXBvbmVudA0KY2x1c2luZm8gPC0gcGFtLnJlc3VsdCRjbHVzaW5mbw0KDQojIENhbGN1bGF0ZSB0aGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMNCnRvdF93aXRoaW5zcyA8LSBzdW0oY2x1c2luZm9bLCAic2l6ZSJdICogY2x1c2luZm9bLCAiYXZfZGlzcyJdXjIpDQoNCiMgUHJpbnQgdGhlIHJlc3VsdA0KcHJpbnQodG90X3dpdGhpbnNzKQ0KYGBgDQpBbiBvdmVybGFwIGJldHdlZW4gY2x1c3RlcnMgaW1wbGllcyB0aGF0IHRoZXJlIGlzIGFtYmlndWl0eSBpbiB0aGUNCmFzc2lnbm1lbnQgb2YgZGF0YSBwb2ludHMgdG8gY2x1c3RlcnMsIGFuZCB0aGUgY2x1c3RlcnMgbWF5IG5vdCBiZQ0Kc3VmZmljaWVudGx5IGRpc3RpbmN0LiBJbiBzdWNoIGNhc2VzLCBpdCBtaWdodCBiZSBuZWVkZWQgdG8gcmVjb25zaWRlcg0KdGhlIG51bWJlciBvZiBjbHVzdGVycyBzaW5jZSB0aGUgZ29hbCBpcyB0byBmaW5kIGEgYmFsYW5jZSwgYnV0IHRvbyBmZXcNCmNsdXN0ZXJzIHJlc3VsdCBpbiBvdmVyc2ltcGxpZmljYXRpb24sIGFzIGluZGljYXRlZCBieSB0aGUgb2JzZXJ2ZWQNCm92ZXJsYXAsIGFuZCBpdCBpcyBldmlkZW50IHRoYXQgZm9ybWluZyBvbmx5IHR3byBjbHVzdGVycyBtYXkgbm90IGJlDQpzdWZmaWNpZW50IHRvIHJlcHJlc2VudCB0aGUgaW5oZXJlbnQgc3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0Lg0KDQoNCiNCQ3ViZWQNCg0KYGBge3J9DQpjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIGMocGFtLnJlc3VsdCRjbHVzdGVyKSANCiANCnNldC5zZWVkKDcpIA0KIA0KIyBTcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB5b3Ugd2FudCB0byBzYW1wbGUgDQpudW1fcm93cyA8LSAxMDAgDQogDQojIFVzZSBzYW1wbGUgd2l0aCB0aGUgc3BlY2lmaWVkIHNlZWQgDQppZHggPC0gc2FtcGxlKDE6ZGltKEhvYmJ5X3Byb2MpWzFdLCBudW1fcm93cykgDQogDQojIFNlbGVjdCB0aGUgc2FtcGxlZCByb3dzIGZyb20gSG9iYnlfcHJvYyANCkhvYmJ5X0RhdGE0IDwtIEhvYmJ5X3Byb2NbaWR4LCBdIA0KDQojIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBjbHVzdGVyIGFzc2lnbm1lbnRzIGFuZCBncm91bmQgdHJ1dGggbGFiZWxzDQpkYXRhc2V0IDwtIGRhdGEuZnJhbWUoY2x1c3RlciA9IGNsdXN0ZXJfYXNzaWdubWVudHMsIGxhYmVsID0gZ3JvdW5kX3RydXRoX2xhYmVscykNCg0KIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsDQpjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MgPC0gZnVuY3Rpb24oZGF0YXNldCkgew0KICBuIDwtIG5yb3coZGF0YXNldCkNCiAgcHJlY2lzaW9uX3N1bSA8LSAwDQogIHJlY2FsbF9zdW0gPC0gMA0KIA0KICBmb3IgKGkgaW4gMTpuKSB7DQogICAgY2x1c3RlciA8LSBkYXRhc2V0JGNsdXN0ZXJbaV0NCiAgICBsYWJlbCA8LSBkYXRhc2V0JGxhYmVsW2ldDQogICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgZnJvbSB0aGUgc2FtZSBjYXRlZ29yeSBpbiBpdHMgY2x1c3Rlcg0KICAgIHNhbWVfY2F0ZWdvcnkgPC0gc3VtKGRhdGFzZXQkbGFiZWxbZGF0YXNldCRjbHVzdGVyID09IGNsdXN0ZXJdID09IGxhYmVsKQ0KICAgDQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGluIGl0cyBjbHVzdGVyDQogICAgc2FtZV9jbHVzdGVyIDwtIHN1bShkYXRhc2V0JGNsdXN0ZXIgPT0gY2x1c3RlcikNCiAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2F0ZWdvcnkNCiAgICB0b3RhbF9zYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsID09IGxhYmVsKQ0KICAgDQogICAgIyBDYWxjdWxhdGUgcHJlY2lzaW9uIGFuZCByZWNhbGwNCiAgICBwcmVjaXNpb25fc3VtIDwtIHByZWNpc2lvbl9zdW0gKyBzYW1lX2NhdGVnb3J5IC8gc2FtZV9jbHVzdGVyDQogICAgcmVjYWxsX3N1bSA8LSByZWNhbGxfc3VtICsgc2FtZV9jYXRlZ29yeSAvIHRvdGFsX3NhbWVfY2F0ZWdvcnkNCiAgfQ0KICAjIEVuZCBsb29wDQogDQogICMgQ2FsY3VsYXRlIGF2ZXJhZ2UgcHJlY2lzaW9uIGFuZCByZWNhbGwNCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbl9zdW0gLyBuDQogIHJlY2FsbCA8LSByZWNhbGxfc3VtIC8gbg0KIA0KICByZXR1cm4obGlzdChwcmVjaXNpb24gPSBwcmVjaXNpb24sIHJlY2FsbCA9IHJlY2FsbCkpDQp9DQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KbWV0cmljcyA8LSBjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MoZGF0YXNldCkNCnByZWNpc2lvbiA8LSBtZXRyaWNzJHByZWNpc2lvbg0KcmVjYWxsIDwtIG1ldHJpY3MkcmVjYWxsDQoNCiMgUHJpbnQgdGhlIHJlc3VsdHMNCmNhdCgiQkN1YmVkIFByZWNpc2lvbj0gIiwgcHJlY2lzaW9uLCAiQU5EIEJDdWJlZCBSZWNhbGw9ICIsIHJlY2FsbCwgIlxuIikNCmBgYA0KDQpUaGUgcmVsYXRpdmVseSBoaWdoIHJlY2FsbCBjb3VsZCBiZSBpbmZsdWVuY2VkIGJ5IHRoZSBzcGVjaWZpYyBjaG9pY2Ugb2YNCnR3byBjbHVzdGVycyBzaW5jZSBpdCBpcyBzZW5zaXRpdmUgdG8gdGhlIG51bWJlciBvZiBjbHVzdGVycy4gSW4gdGhlDQpjb250ZXh0IG9mIGEgdHdvLWNsdXN0ZXIgc29sdXRpb24sIHRoZSByZWNhbGwgc2NvcmUgcmVmbGVjdHMgaG93IHdlbGwNCnRoZSBhbGdvcml0aG0gZ3JvdXBzIGRhdGEgcG9pbnRzIGZyb20gdGhlIHNhbWUgY2xhc3MgaW50byBvbmUgb2YgdGhlIHR3bw0KaWRlbnRpZmllZCBjbHVzdGVycy4gUmVjYWxsIHN1Z2dlc3RzIHRoYXQgYSBzaWduaWZpY2FudCBwb3J0aW9uIG9mIGRhdGENCnBvaW50cyBmcm9tIHRoZSBzYW1lIGdyb3VuZCB0cnV0aCBjbGFzcyBhcmUgaW5kZWVkIGdyb3VwZWQgdG9nZXRoZXIgaW4NCm9uZSBvZiB0aGUgdHdvIGNsdXN0ZXJzLiBIb3dldmVyLCBpdCdzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIGxvdw0KcHJlY2lzaW9uIHNjb3JlICgwLjA0MzIpIGluZGljYXRlcyBhIGxhY2sgb2YgaG9tb2dlbmVpdHkgd2l0aGluIHRoZQ0KaWRlbnRpZmllZCBjbHVzdGVycywgaW1wbHlpbmcgdGhhdCB0aGUgY2x1c3RlcnMgY29udGFpbiBhIG1peCBvZiBkYXRhDQpwb2ludHMgZnJvbSBkaWZmZXJlbnQgZ3JvdW5kIHRydXRoIGNsYXNzZXMuDQoNCg0KDQoNCg0KDQoNCioqQVMgQSBTdW1tYXJ5IEZvciBjbHVzdHJpbmcqKg0KU2lsaG91ZXR0ZSBhbmFseXNpcyBtZWFzdXJlcyBob3cgc2ltaWxhciBhbiBvYmplY3QgaXMgdG8gaXRzIG93biBjbHVzdGVyDQooY29oZXNpb24pIGNvbXBhcmVkIHRvIG90aGVyIGNsdXN0ZXJzIChzZXBhcmF0aW9uKS4gVGhlIHNpbGhvdWV0dGUgd2lkdGgNCnJhbmdlcyBmcm9tIC0xIHRvIDEsIHdoZXJlIGEgaGlnaCB2YWx1ZSBpbmRpY2F0ZXMgdGhhdCB0aGUgb2JqZWN0IGlzDQp3ZWxsIG1hdGNoZWQgdG8gaXRzIG93biBjbHVzdGVyIGFuZCBwb29ybHkgbWF0Y2hlZCB0byBuZWlnaGJvcmluZw0KY2x1c3RlcnMuIEluIG91ciBwcm9qZWN0IHdlIGRpZCBjbHVzdGVycyBmb3Igaz0zLCBrPTQsIGFuZCBrPTIgc2luY2UgaXRzDQpoYXZlIGhpZ2hlciBhdmVyYWdlIHNpbGhvdWV0dGUgdGhlbiBvdGhlciBudW1iZXIuDQoNCkZvciBrPTMsIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggaXMgMC4yMi4gDQpGb3Igaz00LCB0aGUgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGlzIDAuMTkuIA0KRm9yIGs9MiwgdGhlIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aCBpcyAwLjIxLg0KDQpBIGhpZ2hlciBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggZ2VuZXJhbGx5IGluZGljYXRlcyBiZXR0ZXItZGVmaW5lZA0KY2x1c3RlcnMuIFNvLCBpbiB0aGlzIGNhc2UsIGs9MyBoYXMgdGhlIGhpZ2hlc3QgYXZlcmFnZSBzaWxob3VldHRlDQp3aWR0aC4NCg0KQkN1YmVkIGlzIGEgY2x1c3RlcmluZyBldmFsdWF0aW9uIG1ldHJpYyB0aGF0IGNvbnNpZGVycyBib3RoIHByZWNpc2lvbg0KYW5kIHJlY2FsbC4gUHJlY2lzaW9uIG1lYXN1cmVzIHRoZSBhY2N1cmFjeSBvZiB0aGUgcG9zaXRpdmUgcHJlZGljdGlvbnMsDQp3aGlsZSByZWNhbGwgbWVhc3VyZXMgdGhlIGNvdmVyYWdlIG9mIHRoZSBhY3R1YWwgcG9zaXRpdmUgaW5zdGFuY2VzLg0KDQpGb3Igaz0zLCBCQkN1YmVkIFByZWNpc2lvbiA9IDAuMDQ4OCBCQ3ViZWQgUmVjYWxsID0gMC40OTMyDQpGb3Igaz00LEJDdWJlZCBQcmVjaXNpb24gPSAwLjA1MDUgQkN1YmVkIFJlY2FsbCA9IDAuNDEwMQ0KRm9yIGs9MiwgQkN1YmVkUHJlY2lzaW9uID0gMC4wNDMyIEJDdWJlZCBSZWNhbGwgPSAwLjYyNTINCg0KVGhlc2UgbWV0cmljcyBtZWFzdXJlIGhvdyB3ZWxsIHRoZSBjbHVzdGVyaW5nIGFsaWducyB3aXRoIHRoZSBncm91bmQNCnRydXRoLiBIaWdoZXIgcHJlY2lzaW9uIGluZGljYXRlcyBmZXdlciBmYWxzZSBwb3NpdGl2ZXMsIGFuZCBoaWdoZXINCnJlY2FsbCBpbmRpY2F0ZXMgZmV3ZXIgZmFsc2UgbmVnYXRpdmVzLiBIZXJlLCBrPTIgaGFzIHRoZSBoaWdoZXN0IHJlY2FsbA0KKDAuNjI1MiksIGJ1dCBrPTMgaGFzIGEgcmVhc29uYWJsZSBiYWxhbmNlIGJldHdlZW4gcHJlY2lzaW9uIGFuZCByZWNhbGwuDQoNCkluIGNvbmNsdXNpb24sIGs9MyBzZWVtcyB0byBiZSBhIHJlYXNvbmFibGUgY2hvaWNlLiBJdCBoYXMgYSBnb29kDQpzaWxob3VldHRlIHdpZHRoLCBhbmQgaXRzIEJDdWJlZCBQcmVjaXNpb24gYW5kIFJlY2FsbCB2YWx1ZXMgc3RyaWtlIGENCmJhbGFuY2UuDQoNCglLPTIJSz0zKEJFU1QpCUs9NA0KQXZlcmFnZSBTaWxob3VldHRlIHdpZHRoCTAuMjEJMC4yMgkwLjE5DQp0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlCTEwNTkuNg0KCTgzMy41NTg1DQoJNzYyLjg4OTUNCg0KQkN1YmVkIHByZWNpc2lvbgkwLjA0MzIJMC4wNDg4CTAuMDUwNQ0KQkN1YmVkIHJlY2FsbAkwLjYyNTIJMC40OTMyCTAuNDEwMQ0KVmlzdWFsaXphdGlvbglJbiB0aGUgZmlndXJlcyBhYm92ZS4NCg0KDQoqKkZpbmRpbmdzKioNCg0KfCAgICAgICAgICAgICAgICB8IDkwJS8xMCUgfCA5MCUvMTAlICB8ICA5MCUvMTAlICAgfCA4MCUvMjAlIHwgODAlLzIwJSAgfCAgODAlLzIwJSAgIHwgNzAlLzMwJSB8IDcwJS8zMCUgIHwgIDcwJS8zMCUgICB8DQp8Oi0tLS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnwNCnwgICAgICAgICAgICAgICAgfCAgIElHICAgIHwgSUcgcmF0aW8gfCBHaW5pIEluZGV4IHwgICBJRyAgICB8IElHIHJhdGlvIHwgR2luaSBJbmRleCB8ICAgSUcgICAgfCBJRyByYXRpbyB8IEdpbmkgSW5kZXggfA0KfCAgKipBY2N1cmFjeSoqICB8ICAwLjg3MiAgfCAgMC44OTQ0ICB8ICAwLjkxODc1ICAgfCAgMC45MDUgIHwgIDAuODg4ICAgfCAgIDAuOTA2ICAgIHwgIDAuODkzICB8ICAwLjg5MzkgIHwgICAwLjkxMSAgICB8DQp8ICoqcHJlY2lzaW9uKiogIHwgIDAuODU2ICB8ICAwLjg5MiAgIHwgIDAuOTE4NTAgICB8ICAwLjg5OCAgfCAgMC44ODEgICB8ICAgMC45MDUgICAgfCAgMC44ODcgIHwgIDAuODkwICAgfCAgIDAuOTA5ICAgIHwNCnwgKipzZW5zaXRpdml5KiogfCAgMC44NTYgIHwgIDAuODkxICAgfCAgMC45MTkyNyAgIHwgIDAuODk4ICB8ICAwLjg5MCAgIHwgICAwLjkwOSAgICB8ICAwLjg4NyAgfCAgMC45MDIgICB8ICAgMC45MTIgICAgfA0KfCAqKnNwZWNpZmljaXkqKiB8ICAwLjkxOSAgfCAgMC45NDUgICB8ICAgMC45NTc4ICAgfCAgMC45MzcgIHwgIDAuOTQzICAgfCAgIDAuOTUyICAgIHwgIDAuOTI5ICB8ICAwLjk0NyAgIHwgICAwLjk1NCAgICB8DQoNCioqUmVmZXJlbmNlcyoqDQoNCg0KKipXaGF0IGlzIHRoZSBiZXN0IGZvciBvdXIgZGF0YXNldD8qKg0KDQpzaW5jZSBXZSB1c2VkIHRoZSBJbmZvcm1hdGlvbiBHYWluLCBHaW5pIEluZGV4LCBhbmQgR2FpbiBSYXRpbyBhcyB0aHJlZSBpbXBvcnRhbnQgbWV0cmljcyBpbiBvdXIgY2F0ZWdvcmljYWwgZGF0YSBjbGFzc2lmaWNhdGlvbiBhcHByb2FjaCB0byBkZXRlcm1pbmUgaG93IGltcG9ydGFudCBjZXJ0YWluIHZhcmlhYmxlcyB3ZXJlIGluIHByZWRpY3RpbmcgaG9iYnkgY2F0ZWdvcmllcy4gVGhlIGRhdGFzZXQgd2FzIHNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cyBzbyB0aGF0IGRpZmZlcmVudCBzdWJzZXRzIG9mIHRoZSBkYXRhIGNvdWxkIGJlIHVzZWQgdG8gdHJhaW4gY2xhc3NpZmljYXRpb24gYWxnb3JpdGhtcy4gVGhyZWUgbWV0cmljcyB3ZXJlIHVzZWQgdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbDogb3ZlcmFsbCBhY2N1cmFjeSwgcmVjYWxsLCBhbmQgcHJlY2lzaW9uLldlIGV4YW1pbmVkIGFjY3VyYWN5IGF0IHNldmVyYWwgbWVhc3VyZXMgb2YgdHJlZSBzZWxlY3Rpb24sIFByb3ZpZGUgdXMgd2l0aCBtb3JlIGFjY3VyYXRlIHJlc3VsdHMsIHNwZWNpZmljYWxseSBhdCB0aGUgcHJ1bmluZyBwYXJhbWV0ZXJzIG9mIDAuOTAsIDAuOTEsIGFuZCAwLjg5LCBpbiBvcmRlci5UaGUgbWV0aG9kaWNhbCBldmFsdWF0aW9uIHlpZWxkZWQgaW5zaWdodGZ1bCBpbmZvcm1hdGlvbiBvbiBob3cgd2VsbCBlYWNoIHNlbGVjdGlvbiBtZWFzdXJlIHBlcmZvcm1lZCBpbiBwcm9kdWNpbmcgcHJlY2lzZSBwcmVkaWN0aW9ucyB3aXRoaW4gdGhlIGRhdGFzZXQuIGhpZ2ggYWNjdXJhY3kgc3Ryb25nbHkgc3VnZ2VzdCB0aGF0LCBmb3Igb3VyIGRhdGFzZXQsIGNsYXNzaWZpY2F0aW9uIGlzIGEgYmV0dGVyIG1ldGhvZG9sb2d5IHRoYW4gY2x1c3RlcmluZywgc2luY2UgdGhlIGNsYXNzaWZpY2F0aW9uIGdpdmUgdXMgYSBnb29kIHJlc3VsdCB0aGVuIGNsdXN0cmluZy4NCg0KDQo=
=======
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyAqKktpZHMgSG9iYnkgUHJlZGljdGlvbiBEYXRhc2V0KioNCg0KDQoqKlByb2JsZW0qKg0KDQpDaGlsZHJlbiBvZnRlbiBmaW5kIHRoZW1zZWx2ZXMgYXQgYSBjcm9zc3JvYWRzIHdoZW4gaXQgY29tZXMgdG8gZGlzY292ZXJpbmcgdGhlaXIgcGFzc2lvbnMgb3IgaG9iYmllcywgYmUgaXQgaW4gYWNhZGVtaWNzLCBhcnRzLCBvciBzcG9ydHMuIFJlY29nbml6aW5nIHRoZSBpbXBvcnRhbmNlIG9mIGd1aWRpbmcgY2hpbGRyZW4gdG93YXJkcyBhY3Rpdml0aWVzIHRoZXkgYXJlIHBhc3Npb25hdGUgYWJvdXQ7IHdlIGhhdmUgY3VyYXRlZCBhIGRhdGFzZXQgb2J0YWluZWQgdGhyb3VnaCBzdXJ2ZXlzIGNvbmR1Y3RlZCB3aXRoIHBhcmVudHMuIFRoaXMgZGF0YXNldCBjb21waWxlcyB2YWx1YWJsZSBpbmZvcm1hdGlvbiBhYm91dCBjaGlsZHJlbidzIHByZWZlcmVuY2VzLCBlbmFibGluZyB0aGUgY3JlYXRpb24gb2YgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbCBhaW1lZCBhdCBwcmVkaWN0aW5nIGtpZHMnIGhvYmJpZXMuIA0KDQpJbiB0aGlzIGV4cGxvcmF0b3J5IGpvdXJuZXksIHdlIGRlbHZlIGludG8gdGhlIGRhdGFzZXQgdG8gdW5jb3ZlciBwYXR0ZXJucyBhbmQgaW5zaWdodHMgdGhhdCBjYW4gYXNzaXN0IHBhcmVudHMgaW4gdW5kZXJzdGFuZGluZyB0aGVpciBjaGlsZCdzIGluY2xpbmF0aW9ucyBiZXR0ZXIuIFRocm91Z2ggdGhlIGFwcGxpY2F0aW9uIG9mIGNsdXN0ZXJpbmcgdGVjaG5pcXVlcywgd2UgYWltIHRvIGNhdGVnb3JpemUgY2hpbGRyZW4gYmFzZWQgb24gbnVtYmVyIG9mIGNvbHVtbnMgKEZhdl9zdWIgLCBTY2hvbGFyc2hpcCwgZXRjLi4pLiBUaGUgdWx0aW1hdGUgZ29hbCBpcyB0byBwcm92aWRlIHBhcmVudHMgd2l0aCBtZWFuaW5nZnVsIHJlY29tbWVuZGF0aW9ucywgZm9zdGVyaW5nIGFuIGVudmlyb25tZW50IHdoZXJlIGNoaWxkcmVuIGNhbiB0aHJpdmUgaW4gYWN0aXZpdGllcyB0aGF0IGdlbnVpbmVseSByZXNvbmF0ZSB3aXRoIHRoZWlyIGludGVyZXN0cy4gDQoNCkJ5IGFuYWx5emluZyB0aGlzIGRhdGFzZXQsIHdlIGhvcGUgdG8gb2ZmZXIgaW5zaWdodGZ1bCBpbmZvcm1hdGlvbiB0aGF0IHdpbGwgYWlkIHBhcmVudHMgaW4gZ3VpZGluZyB0aGVpciBraWRzIHRvd2FyZCB0aGUgcmlnaHQgaG9iYmllcy4gDQoNCg0KKipEYXRhIE1pbmluZyBUYXNrKioNCg0KVGhlIGRhdGEgbWluaW5nIHRhc2sgYXQgaGFuZCByZXZvbHZlcyBhcm91bmQgcHJlZGljdGluZyBraWRzJyBob2JiaWVzIGJhc2VkIG9uIGEgZGF0YXNldCBuYW1lZCAiSG9iYnlfRGF0YSwiIG9idGFpbmVkIHRocm91Z2ggc3BlY2lmaWMgcXVlc3Rpb25zIHBvc2VkIHRvIHRoZWlyIHBhcmVudHMgcmVnYXJkaW5nIHByZWZlcmVuY2VzLCBjYXBhYmlsaXRpZXMsIGFuZCBhY2hpZXZlbWVudHMuIFRoaXMgZGF0YXNldCBzZXJ2ZXMgYXMgdGhlIGZvdW5kYXRpb24gZm9yIHR3byBwcmltYXJ5IGRhdGEgbWluaW5nIHRhc2tzOiBjbGFzc2lmaWNhdGlvbiBhbmQgY2x1c3RlcmluZy4gSW4gdGhlIGNsYXNzaWZpY2F0aW9uIHByb2Nlc3MsIHRoZSBnb2FsIGlzIHRvIHRyYWluIGEgbWFjaGluZSBsZWFybmluZyBtb2RlbCB0byBiZSBjYXBhYmxlIG9mIGFjY3VyYXRlbHkgcHJlZGljdGluZyBhIGNoaWxkJ3MgaG9iYnkgYXMgZWl0aGVyICJhY2FkZW1pYywiICJhcnQsIiBvciAic3BvcnRzLiIgVGhpcyByZXF1aXJlcyB1c2luZyB0aGUgZGF0YSBjb2xsZWN0ZWQgZnJvbSBwYXJlbnRzIHRvIGVzdGFibGlzaCBwYXR0ZXJucyBhbmQgcmVsYXRpb25zaGlwcyB0aGF0IGd1aWRlIHRoZSBtb2RlbCBpbiBtYWtpbmcgYWNjdXJhdGUgcHJlZGljdGlvbnMuIENvbmN1cnJlbnRseSwgdGhlIGNsdXN0ZXJpbmcgcHJvY2VzcyBpbnZvbHZlcyBwYXJ0aXRpb25pbmcgdGhlIGRhdGFzZXQgaW50byBtZWFuaW5nZnVsIGNsdXN0ZXJzLCBncm91cGluZyB0b2dldGhlciBjaGlsZHJlbiB3aXRoIHNpbWlsYXIgY2hhcmFjdGVyaXN0aWNzIG9yIHByZWZlcmVuY2VzLiBUaHJvdWdoIHRoZXNlIGR1YWwgYXBwcm9hY2hlcywgdGhlIGRhdGEgbWluaW5nIHRhc2sgYWltcyB0byBkZXZlbG9wIGEgcm9idXN0IHByZWRpY3RpdmUgbW9kZWwgY2FwYWJsZSBvZiBhY2N1cmF0ZWx5IGNhdGVnb3JpemluZyBjaGlsZHJlbidzIGhvYmJpZXMgaW4gb3JkZXIgdG8gdW5jb3ZlciB0aGUgaW5oZXJlbnQgc3RydWN0dXJlcywgcGF0dGVybnMsIGFuZCBhc3NvY2lhdGlvbnMgd2l0aGluIHRoZSBkYXRhc2V0LCBjb250cmlidXRpbmcgdG8gYSBkZWVwZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGl2ZXJzZSBpbnRlcmVzdHMgYW5kIGVuZ2FnZW1lbnQgbGV2ZWxzIG9mIHRoZSB5b3VuZyBwb3B1bGF0aW9uIGluIGFjYWRlbWljLCBhcnRpc3RpYywgYW5kIHNwb3J0cy1yZWxhdGVkIGFjdGl2aXRpZXMuDQogDQogDQoNCg0KKipEYXRhKioNCg0KVGhlIHNvdXJjZTogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9hYnRhYm0vaG9iYnktcHJlZGljdGlvbi1iYXNpYyANCk51bWJlciBvZiBvYmplY3RzOiAxNjAxIA0KDQpOdW1iZXIgb2YgYXR0cmlidXRlczogMTQgDQoNCg0KKipEZXNjcmlwdGlvbjoqKg0KRm9sbG93aW5nIHRoZSBzZWxlY3Rpb24gb2Ygb3VyIGRhdGEgc2V0ICgiSG9iYnlfRGF0YSIpIHdoaWNoIHByZWRpY3RzIGtpZHMnIGhvYmJpZXMsIHRoYXQgd2FzIGNvbGxlY3RlZCBieSBhc2tpbmcgdGhlaXIgcGFyZW50cyBzcGVjaWZpYyBxdWVzdGlvbnMgYWJvdXQgdGhlaXIga2lkJ3MgcHJlZmVyZW5jZXMsIGNhcGFiaWxpdGllcywgYW5kIGFjaGlldmVtZW50cy4gVG8gaGVscCB1cyB0cmFpbiB0aGUgbWFjaGluZSB0byBwcmVkaWN0IHRoZSBraWQncyBob2JieS4gV2Ugd2lsbCBiZWdpbiB0byBwcmVwcm9jZXNzIGFuZCBhbmFseXplIHRoZSBkYXRhLg0KDQoNCg0KKipBdHRyaWJ1dGUqKiAqKkRlc2NyaXB0aW9uOioqDQoNCnwgQXR0cmlidXRlIG5hbWUgICAgICAgICB8ICoqRGVzY3JpcHRpb24qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAqKkRhdGEgdHlwZSoqIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLXwNCnwgT2x5bXBpYWRfUGFydGljaXBhdGlvbiB8IEhhcyB5b3VyIGNoaWxkIHBhcnRpY2lwYXRlZCBpbiBhbnkgU2NpZW5jZS9NYXRocyAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgU2Nob2xhcnNoaXAgICAgICAgICAgICB8IEhhcyBoZS9zaGUgcmVjZWl2ZWQgYW55IHNjaG9sYXJzaGlwPyAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgU2Nob29sICAgICAgICAgICAgICAgICB8IExvdmUncyBnb2luZyB0byBzY2hvb2w/ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgRmF2X3N1YiAgICAgICAgICAgICAgICB8IFdoYXQgaXMgaGlzL2hlciBmYXZvcml0ZSBzdWJqZWN0PyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBDYXRlZ29yaWNhbCAgIHwNCnwgUHJvamVjdHMgICAgICAgICAgICAgICB8IEhhcyBkb25lIGFueSBwcm9qZWN0cyB1bmRlciBhY2FkZW1pY3MgYmVmb3JlPyAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgR3Jhc3BfcG93ICAgICAgICAgICAgICB8IEhpcy9IZXIgR3Jhc3BpbmcgcG93ZXIgKDEtNikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgVGltZV9zcHJ0ICAgICAgICAgICAgICB8IEhvdyBtdWNoIHRpbWUgZG9lcyBoZS9zaGUgc3BlbmQgcGxheWluZyBvdXRkb29yL2luZG9vciBnYW1lcz8gfCBPcmRpbmFsICAgICAgIHwNCnwgTWVkYWxzICAgICAgICAgICAgICAgICB8IE1lZGFscyB3b24gaW4gU3BvcnRzPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgQ2FyZWVyX3NwcnQgICAgICAgICAgICB8IFdhbnQncyB0byBwdXJzdWUgaGlzL2hlciBjYXJlZXIgaW4gc3BvcnRzPyAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgQWN0X3NwcnQgICAgICAgICAgICAgICB8IFJlZ3VsYXIgaW4gaGlzL2hlciBzcG9ydHMgYWN0aXZpdGllcz8gICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgRmFudF9hcnRzICAgICAgICAgICAgICB8IExvdmUgY3JlYXRpbmcgZmFudGFzeSBwYWludGluZ3M/ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgV29uX2FydHMgICAgICAgICAgICAgICB8IFdvbiBhcnQgY29tcGV0aXRpb25zPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgVGltZV9hcnQgICAgICAgICAgICAgICB8IFRpbWUgdXRpbGl6ZWQgaW4gQXJ0cz8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgUHJlZGljdGVkIEhvYmJ5ICAgICAgICB8IHByZWRpY3Rpb25zIGZvciB0aGUgaG9iYnkgdGhhdCB0aGUga2lkIHdvdWxkbCBpa2UgICAgICAgICAgICAgfCBDYXRlZ29yaWNhbCAgIHwNCj09PT09PT0NCj4+Pj4+Pj4gU3Rhc2hlZCBjaGFuZ2VzDQoNCioqR2VuZXJhbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YSBzZXQ6KioNCg0KYGBge3J9DQpzdHIoSG9iYnlfRGF0YSkNCmBgYA0KDQotICAgKipzYW1wbGVzIG9mIHJhdyBkYXRhc2V0OioqDQoNCmBgYHtyfQ0Kc2FtcGxlKEhvYmJ5X0RhdGEpDQpgYGANCg0KLSAgICoqdmFyaWFibGVzIGRpc3RyaWJ1dGlvbjoqKg0KDQogICAgSW4gb3VyIGRhdGFzZXQsIG51bWVyaWMgdmFyaWFibGVzIGFyZSBub3QgYXZhaWxhYmxlOyBpbnN0ZWFkLCB3ZSBoYXZlIHRocmVlIG9yZGluYWwgdmFyaWFibGVzLiBEdWUgdG8gdGhlIG5hdHVyZSBvZiBvdXIgZGF0YSB0eXBlcywgY2VydGFpbiB0eXBlcyBvZiBncmFwaHMsIHN1Y2ggYXMgc2NhdHRlciBwbG90cyBhbmQgYm94IHBsb3RzLCB3ZXJlIG5vdCBzdWl0YWJsZSBmb3Igb3VyIGFuYWx5c2lzLg0KDQp2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIFRpbWVfc3BydDoNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJtYWdyaXR0ciIpICMgaW5zdGFsbCBvbmx5IG9uZSB0aW1lIHRoZW4gcHV0IHRoaXMgY29tbWFuZCBhcyBjb21tZW50IGFmdGVyIGluc3RhbGxhdGlvbg0KbGlicmFyeShtYWdyaXR0cikgIyMgZm9yIHBpcGUgb3BlcmF0aW9ucw0KSG9iYnlfRGF0YSRUaW1lX2FydCAlPiUgZGVuc2l0eSgpICU+JSBwbG90KG1haW49J3ZhcmlhYmxlcyBkaXN0cmlidXRpb24gb2YgVGltZV9hcnQnKQ0KDQpgYGANCg0KSW4gdGhlICJUaW1lX2FydCIgdmFyaWFibGUsIHBhcmVudHMgd2VyZSByZXF1ZXN0ZWQgdG8gYXNzZXNzIHRoZSB0aW1lIHRoZWlyIGNoaWxkIGRlZGljYXRlZCB0byBhcnRpc3RpYyBwdXJzdWl0cyBsaWtlIHBhaW50aW5nIG9yIHBhcGVyIGNyYWZ0aW5nLCB1c2luZyBhIHNjYWxlIHJhbmdpbmcgZnJvbSAxIHRvIDYsIHdoZXJlIDYgcmVwcmVzZW50cyB0aGUgaGlnaGVzdCBsZXZlbCBvZiBpbnZvbHZlbWVudC4gSXQncyB3b3J0aCBub3RpbmcgdGhhdCB0aGUgY29uY2VudHJhdGlvbiBvZiBsb3dlciByYXRpbmdzIGF0IHRoZSBsb3dlciBlbmQgb2YgdGhlIHNjYWxlICgxKSBpcyBxdWl0ZSBwcm9ub3VuY2VkLCBhbmQgdGhpcyB0ZW5kZW5jeSBtYXkgYmUgYXR0cmlidXRlZCB0byB0aGUgaW5oZXJlbnQgaW5jbGluYXRpb24gb2YgY2hpbGRyZW4gdG93YXJkcyBwaHlzaWNhbCBhY3Rpdml0aWVzLg0KDQp2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIFRpbWVfYXJ0Og0KDQpgYGB7cn0NCkhvYmJ5X0RhdGEkVGltZV9zcHJ0ICU+JSBkZW5zaXR5KCkgJT4lIHBsb3QobWFpbj0ndmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiBUaW1lX3NwcnQnKQ0KYGBgDQoNClBhcmVudHMgd2VyZSByZXF1ZXN0ZWQgdG8gYXNzZXNzIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gc3BvcnRzIG9uIGEgc2NhbGUgZnJvbSAxIHRvIDYgd2l0aGluIHRoZSAiVGltZV9zcHJ0IiB2YXJpYWJsZS4gTm90YWJseSwgdGhlIG1vc3QgcHJldmFsZW50IHJhbmtpbmcgd2FzIDMsIHN1Z2dlc3RpbmcgYSBtb2RlcmF0ZSBsZXZlbCBvZiBzcG9ydHMgcGFydGljaXBhdGlvbi4gSXQncyBpbnRlcmVzdGluZyB0byBvYnNlcnZlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBleGhpYml0cyBhIHNoYXBlIGFraW4gdG8gYSBiZWxsIGN1cnZlLCBpbmRpY2F0aW5nIHRoYXQgYSBzdWJzdGFudGlhbCBwcm9wb3J0aW9uIG9mIGNoaWxkcmVuIGhhdmUgYSBnZW51aW5lIGxvdmUgZm9yIHNwb3J0cy4NCg0KdmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiBHcmFzcF9wb3c6DQoNCmBgYHtyfQ0KSG9iYnlfRGF0YSRHcmFzcF9wb3cgJT4lIGRlbnNpdHkoKSAlPiUgcGxvdChtYWluPSd2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIEdyYXNwX3BvdycpDQpgYGANCg0KVGhlIGRlbnNpdHkgZ3JhcGggZGVwaWN0aW5nIHBhcmVudHMnIHJhdGluZ3Mgb2YgdGhlaXIgY2hpbGRyZW4ncyBncmFzcCBwb3dlciwgd2hpY2ggcmFuZ2VzIGZyb20gMSB0byA2LCBpbGx1c3RyYXRlcyBhIHRyZW5kIHdoZXJlIHRoZSBtb3N0IGNvbW1vbiByYXRpbmcgaXMgbGV2ZWwgMywgZm9sbG93ZWQgYnkgbGV2ZWwgNCwgbGV2ZWwgNSwgbGV2ZWwgMiwgbGV2ZWwgMSwgYW5kIGxldmVsIDYuIFRoaXMgZGlzdHJpYnV0aW9uIGluZGljYXRlcyB0aGF0IGEgc3Vic3RhbnRpYWwgcHJvcG9ydGlvbiBvZiBwYXJlbnRzIGJlbGlldmUgdGhlaXIgY2hpbGRyZW4gcG9zc2VzcyBncmFzcCBwb3dlciB0aGF0IGlzIGF2ZXJhZ2Ugb3Igc2xpZ2h0bHkgYWJvdmUgYXZlcmFnZSAobGV2ZWxzIDMgYW5kIDQpLCB3aXRoIGZld2VyIGNoaWxkcmVuIHJhdGVkIGF0IHRoZSBleHRyZW1lcyAobGV2ZWxzIDEsIDIsIDUsIGFuZCA2KS4NCg0KdmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiB0aGUgY2xhc3MgbGFiZWwgJ1ByZWRpY3RlZCBIb2JieSc6DQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKSAjIGluc3RhbGwgb25seSBvbmUgdGltZSB0aGVuIHB1dCB0aGlzIGNvbW1hbmQgYXMgY29tbWVudCBhZnRlciBpbnN0YWxsYXRpb24NCmxpYnJhcnkoZHBseXIpDQoNCmRhdGFzZXQyIDwtIEhvYmJ5X0RhdGEgJT4lIHNhbXBsZV9uKDE2MDApDQp0YWJsZShkYXRhc2V0MiRgUHJlZGljdGVkIEhvYmJ5YCkgJT4lIHBpZSgpDQp0YWIgPC0gZGF0YXNldDIkYFByZWRpY3RlZCBIb2JieWAgJT4lIHRhYmxlKCkNCnByZWNlbnRhZ2VzIDwtIHRhYiAlPiUgcHJvcC50YWJsZSgpICU+JSByb3VuZCgzKSAqIDEwMCANCnR4dCA8LSBwYXN0ZTAobmFtZXModGFiKSwgJ1xuJywgcHJlY2VudGFnZXMsICclJykNCnBpZSh0YWIsIGxhYmVscz10eHQpDQpgYGANCg0KVGhlIHBpZSBjaGFydCBpbGx1c3RyYXRlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBjbGFzcyBsYWJlbCAnUHJlZGljdGVkIEhvYmJ5Jy4gSXQncyBldmlkZW50IHRoYXQgYSBzdWJzdGFudGlhbCBwb3J0aW9uLCBhcHByb3hpbWF0ZWx5IDQzLjclLCBvZiB0aGUgY2hpbGRyZW4ncyBob2JiaWVzIGFyZSBhY2FkZW1pYyBpbiBuYXR1cmUsIGluZGljYXRpbmcgYSBzdHJvbmcgaW50ZXJlc3QgaW4gZWR1Y2F0aW9uYWwgcHVyc3VpdHMuIEFkZGl0aW9uYWxseSwgMzAuOCUgb2YgdGhlIGtpZHMgYXJlIGVuZ2FnZWQgaW4gc3BvcnRzLCByZWZsZWN0aW5nIGEgc2lnbmlmaWNhbnQgaW5jbGluYXRpb24gdG93YXJkcyBwaHlzaWNhbCBhY3Rpdml0aWVzLiBBcnRzLXJlbGF0ZWQgaG9iYmllcyBhY2NvdW50IGZvciAyNS42JSBvZiB0aGUgdG90YWwsIHRoZSBkaXN0cmlidXRpb24gcmVmbGVjdHMgYSBoYXJtb25pb3VzIGJsZW5kIG9mIGhvYmJpZXMuIFRoaXMgYmFsYW5jZWQgZGlzdHJpYnV0aW9uIG5vdCBvbmx5IHNpZ25pZmllcyBhIHZhcmlldHkgb2YgaW50ZXJlc3RzIGJ1dCBhbHNvIGluZGljYXRlcyBhIHdlbGwtcm91bmRlZCBlbmdhZ2VtZW50IG9mIGNoaWxkcmVuIGluIGFjYWRlbWljLCBwaHlzaWNhbCwgYW5kIGNyZWF0aXZlIGFjdGl2aXRpZXMuDQoNCg0KKipTdGF0aXN0aWNhbCBtZWFzdXJlcyA6KioNCg0KYGBge3J9DQpmaW5kX21vZGUgPC0gZnVuY3Rpb24oeCkgew0KICB1IDwtIHVuaXF1ZSh4KQ0KICB0YWIgPC0gdGFidWxhdGUobWF0Y2goeCwgdSkpDQogIHVbdGFiID09IG1heCh0YWIpXQ0KfQ0KDQpmaW5kX21vZGUoSG9iYnlfRGF0YSRUaW1lX2FydCkNCiMgVXBkYXRlZCB1cHN0cmVhbQ0KaGlzdChIb2JieV9EYXRhJFRpbWVfYXJ0KQ0KDQojU3Rhc2hlZCBjaGFuZ2VzDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBtYWtlcyBpdCBldmlkZW50IHRoYXQgdGhlIG1vZGUgaXMgZXF1YWwgdG8gMSwgYW5kIHdlIGJlbGlldmUgdGhhdCB0aGUgaGlnaCBmcmVxdWVuY3kgb2YgcGFyZW50cyByYW5raW5nICIxIiBhcyB0aGUgbW9zdCBjaG9zZW4gcmFuayBpbiB0aGUgIlRpbWVfYXJ0IiB2YXJpYWJsZSBjb3VsZCBiZSBhdHRyaWJ1dGVkIHRvIHNldmVyYWwgZmFjdG9ycy4gSXQgbWlnaHQgaW5kaWNhdGUgdGhhdCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBwYXJlbnRzIHBlcmNlaXZlIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gYXJ0IGFjdGl2aXRpZXMgYXMgcmVsYXRpdmVseSBsb3csIHBvc3NpYmx5IGR1ZSB0byB0aW1lIGNvbnN0cmFpbnRzLCBhY2FkZW1pYyBwcmlvcml0aWVzLCBvciBhIGxpbWl0ZWQgaW50ZXJlc3QgaW4gYXJ0LiBBbHRlcm5hdGl2ZWx5LCBpdCBjb3VsZCByZWZsZWN0IHRoYXQgcGFyZW50cyB2YWx1ZSBhIG1vcmUgYmFsYW5jZWQgYXBwcm9hY2ggdG8gdGhlaXIgY2hpbGRyZW4ncyBhY3Rpdml0aWVzLCB3aXRoIGEgdmFyaWV0eSBvZiBpbnRlcmVzdHMgYW5kIHJlc3BvbnNpYmlsaXRpZXMgc2hhcmluZyB0aGVpciB0aW1lLiBUaGlzIHRyZW5kIGNvdWxkIGFsc28gcmVzdWx0IGZyb20gYSBjdWx0dXJhbCBvciBlZHVjYXRpb25hbCBlbXBoYXNpcyBvbiBvdGhlciBzdWJqZWN0cyBhbmQgZXh0cmFjdXJyaWN1bGFyIGFjdGl2aXRpZXMgdGhhdCBjb21wZXRlIGZvciBhIGNoaWxkJ3MgdGltZSwgcG90ZW50aWFsbHkgbGVhZGluZyB0byBhIGxvd2VyIHJhbmtpbmcgZm9yIGFydC1yZWxhdGVkIGFjdGl2aXRpZXMuDQoNCmBgYHtyfQ0KZmluZF9tb2RlKEhvYmJ5X0RhdGEkR3Jhc3BfcG93KQ0KaGlzdChIb2JieV9EYXRhJEdyYXNwX3BvdykNCmBgYA0KDQpSYW5rIDMsIGluIHRoaXMgY29udGV4dCwgbWF5IGhhdmUgYmVlbiB0aGUgbW9zdCBjaG9zZW4gcmFuayBiZWNhdXNlIGl0IGxpa2VseSByZXByZXNlbnRzIGFuIGF2ZXJhZ2Ugb3IgbW9kZXJhdGUgbGV2ZWwgb2YgZ3Jhc3AgcG93ZXIuIFBhcmVudHMgbWF5IGhhdmUgYXNzZXNzZWQgdGhlaXIgY2hpbGRyZW4ncyBncmFzcCBwb3dlciBhcyBuZWl0aGVyIGV4Y2VwdGlvbmFsbHkgc3Ryb25nIChyYW5rIDUgb3IgNikgbm9yIHBhcnRpY3VsYXJseSB3ZWFrIChyYW5rIDEgb3IgMiksIHJlc3VsdGluZyBpbiB0aGUgcHJlZmVyZW5jZSBmb3IgdGhlIG1pZGRsZS1yYW5raW5nIG9wdGlvbi4gVGhpcyBjaG9pY2UgY291bGQgcmVmbGVjdCBhIHBlcmNlcHRpb24gdGhhdCB0aGVpciBjaGlsZHJlbidzIGdyYXNwIHBvd2VyIGZhbGxzIHdpdGhpbiBhIHR5cGljYWwgb3IgZXhwZWN0ZWQgcmFuZ2UsIG1ha2luZyBpdCB0aGUgbW9zdCBjb21tb24gcmF0aW5nLg0KDQpgYGB7cn0NCmZpbmRfbW9kZShIb2JieV9EYXRhJFRpbWVfc3BydCkNCmhpc3QoSG9iYnlfRGF0YSRUaW1lX3NwcnQpDQpgYGANCg0KQXMgc2hvd24gaW4gdGhlIGhpc3RvZ3JhbSB0aGUgcmFuayAzIG9uIGEgc2NhbGUgb2YgMSB0byA2IHdhcyBsaWtlbHkgdGhlIG1vc3QgY2hvc2VuIHJhbmsgZm9yIGFzc2Vzc2luZyBjaGlsZHJlbidzIGludm9sdmVtZW50IGluIHNwb3J0cyBiZWNhdXNlIGl0IHJlcHJlc2VudHMgYSBiYWxhbmNlZCBtaWRkbGUgZ3JvdW5kLiBQYXJlbnRzIG1heSBoYXZlIHBlcmNlaXZlZCByYW5rIDMgYXMgaW5kaWNhdGluZyB0aGF0IHRoZWlyIGNoaWxkcmVuIGFyZSBtb2RlcmF0ZWx5IGludm9sdmVkIGluIHNwb3J0cywgbm90IGV4Y2Vzc2l2ZWx5IGNvbW1pdHRlZCBvciBkaXNpbnRlcmVzdGVkLiBUaGlzIG1pZGRsZS1vZi10aGUtcm9hZCByYW5raW5nIHJlZmxlY3RzIGEgY29tbW9uIHBlcnNwZWN0aXZlIHRoYXQgbWFueSBwYXJlbnRzIG1heSBob2xkLCBjb25zaWRlcmluZyB0aGF0IGV4dHJlbWUgcmFua2luZ3MsIHN1Y2ggYXMgMSBvciA2LCBtaWdodCBzdWdnZXN0IGVpdGhlciBhIGxhY2sgb2YgaW52b2x2ZW1lbnQgb3IgYW4gZXhjZXNzaXZlIGZvY3VzIG9uIHNwb3J0cywgd2hpY2ggbWF5IG5vdCBhbGlnbiB3aXRoIHRoZWlyIHBlcmNlcHRpb24gb2YgdGhlaXIgY2hpbGQncyBvdmVyYWxsIHdlbGwtcm91bmRlZCBkZXZlbG9wbWVudC4NCg0KVGhlIGhpc3RvZ3JhbSBncmFwaCBmb3IgIlRpbWVfYXJ0IiB2YXJpYWJsZQ0KYGBge3J9DQpoaXN0KEhvYmJ5X0RhdGEkVGltZV9hcnQpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBtYWtlcyBpdCBldmlkZW50IHRoYXQgdGhlIG1vZGUgaXMgZXF1YWwgdG8gMSwgYW5kIHdlIGJlbGlldmUgdGhhdCB0aGUgaGlnaCBmcmVxdWVuY3kgb2YgcGFyZW50cyByYW5raW5nICIxIiBhcyB0aGUgbW9zdCBjaG9zZW4gcmFuayBpbiB0aGUgIlRpbWVfYXJ0IiB2YXJpYWJsZSBjb3VsZCBiZSBhdHRyaWJ1dGVkIHRvIHNldmVyYWwgZmFjdG9ycy4gSXQgbWlnaHQgaW5kaWNhdGUgdGhhdCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBwYXJlbnRzIHBlcmNlaXZlIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gYXJ0IGFjdGl2aXRpZXMgYXMgcmVsYXRpdmVseSBsb3csIHBvc3NpYmx5IGR1ZSB0byB0aW1lIGNvbnN0cmFpbnRzLCBhY2FkZW1pYyBwcmlvcml0aWVzLCBvciBhIGxpbWl0ZWQgaW50ZXJlc3QgaW4gYXJ0LiBBbHRlcm5hdGl2ZWx5LCBpdCBjb3VsZCByZWZsZWN0IHRoYXQgcGFyZW50cyB2YWx1ZSBhIG1vcmUgYmFsYW5jZWQgYXBwcm9hY2ggdG8gdGhlaXIgY2hpbGRyZW4ncyBhY3Rpdml0aWVzLCB3aXRoIGEgdmFyaWV0eSBvZiBpbnRlcmVzdHMgYW5kIHJlc3BvbnNpYmlsaXRpZXMgc2hhcmluZyB0aGVpciB0aW1lLiBUaGlzIHRyZW5kIGNvdWxkIGFsc28gcmVzdWx0IGZyb20gYSBjdWx0dXJhbCBvciBlZHVjYXRpb25hbCBlbXBoYXNpcyBvbiBvdGhlciBzdWJqZWN0cyBhbmQgZXh0cmFjdXJyaWN1bGFyIGFjdGl2aXRpZXMgdGhhdCBjb21wZXRlIGZvciBhIGNoaWxkJ3MgdGltZSwgcG90ZW50aWFsbHkgbGVhZGluZyB0byBhIGxvd2VyIHJhbmtpbmcgZm9yIGFydC1yZWxhdGVkIGFjdGl2aXRpZXMuDQoNCg0KYGBge3J9DQpmaW5kX21vZGUoSG9iYnlfRGF0YSRHcmFzcF9wb3cpDQpgYGANClRoZSBoaXN0b2dyYW0gZ3JhcGggZm9yICJHcmFzcF9wb3ciIHZhcmlhYmxlDQpgYGB7cn0NCmhpc3QoSG9iYnlfRGF0YSRHcmFzcF9wb3cpDQpgYGANClJhbmsgMywgaW4gdGhpcyBjb250ZXh0LCBtYXkgaGF2ZSBiZWVuIHRoZSBtb3N0IGNob3NlbiByYW5rIGJlY2F1c2UgaXQgbGlrZWx5IHJlcHJlc2VudHMgYW4gYXZlcmFnZSBvciBtb2RlcmF0ZSBsZXZlbCBvZiBncmFzcCBwb3dlci4gUGFyZW50cyBtYXkgaGF2ZSBhc3Nlc3NlZCB0aGVpciBjaGlsZHJlbidzIGdyYXNwIHBvd2VyIGFzIG5laXRoZXIgZXhjZXB0aW9uYWxseSBzdHJvbmcgKHJhbmsgNSBvciA2KSBub3IgcGFydGljdWxhcmx5IHdlYWsgKHJhbmsgMSBvciAyKSwgcmVzdWx0aW5nIGluIHRoZSBwcmVmZXJlbmNlIGZvciB0aGUgbWlkZGxlLXJhbmtpbmcgb3B0aW9uLiBUaGlzIGNob2ljZSBjb3VsZCByZWZsZWN0IGEgcGVyY2VwdGlvbiB0aGF0IHRoZWlyIGNoaWxkcmVuJ3MgZ3Jhc3AgcG93ZXIgZmFsbHMgd2l0aGluIGEgdHlwaWNhbCBvciBleHBlY3RlZCByYW5nZSwgbWFraW5nIGl0IHRoZSBtb3N0IGNvbW1vbiByYXRpbmcuDQoNCg0KYGBge3J9DQpmaW5kX21vZGUoSG9iYnlfRGF0YSRUaW1lX3NwcnQpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBncmFwaCBmb3IgIlRpbWVfc3BydCIgdmFyaWFibGUNCmBgYHtyfQ0KaGlzdChIb2JieV9EYXRhJFRpbWVfc3BydCkNCmBgYA0KQXMgc2hvd24gaW4gdGhlIGhpc3RvZ3JhbSB0aGUgcmFuayAzIG9uIGEgc2NhbGUgb2YgMSB0byA2IHdhcyBsaWtlbHkgdGhlIG1vc3QgY2hvc2VuIHJhbmsgZm9yIGFzc2Vzc2luZyBjaGlsZHJlbidzIGludm9sdmVtZW50IGluIHNwb3J0cyBiZWNhdXNlIGl0IHJlcHJlc2VudHMgYSBiYWxhbmNlZCBtaWRkbGUgZ3JvdW5kLiBQYXJlbnRzIG1heSBoYXZlIHBlcmNlaXZlZCByYW5rIDMgYXMgaW5kaWNhdGluZyB0aGF0IHRoZWlyIGNoaWxkcmVuIGFyZSBtb2RlcmF0ZWx5IGludm9sdmVkIGluIHNwb3J0cywgbm90IGV4Y2Vzc2l2ZWx5IGNvbW1pdHRlZCBvciBkaXNpbnRlcmVzdGVkLiBUaGlzIG1pZGRsZS1vZi10aGUtcm9hZCByYW5raW5nIHJlZmxlY3RzIGEgY29tbW9uIHBlcnNwZWN0aXZlIHRoYXQgbWFueSBwYXJlbnRzIG1heSBob2xkLCBjb25zaWRlcmluZyB0aGF0IGV4dHJlbWUgcmFua2luZ3MsIHN1Y2ggYXMgMSBvciA2LCBtaWdodCBzdWdnZXN0IGVpdGhlciBhIGxhY2sgb2YgaW52b2x2ZW1lbnQgb3IgYW4gZXhjZXNzaXZlIGZvY3VzIG9uIHNwb3J0cywgd2hpY2ggbWF5IG5vdCBhbGlnbiB3aXRoIHRoZWlyIHBlcmNlcHRpb24gb2YgdGhlaXIgY2hpbGQncyBvdmVyYWxsIHdlbGwtcm91bmRlZCBkZXZlbG9wbWVudC4NCg0KDQoqKkRhdGEgcHJlcHJvY2Vzc2luZyoqDQoNCg0KKiojMSMgRGF0YSBjbGVhbmluZzoqKg0KDQpEdXJpbmcgdGhlIGRhdGEgY2xlYW5pbmcgc3RhZ2UsIGZpbmRpbmcgYW5kIGZpeGluZyBmYXVsdHMsIGluY29uc2lzdGVuY2llcywgYW5kIGVycm9ycyBpbiBhIGRhdGFzZXQgaGVscHMgaXQgYmUgbW9yZSByZWxpYWJsZSBhbmQgb2YgaGlnaGVyIHF1YWxpdHkgZm9yIGFuYWx5c2lzIGFuZCBtb2RlbGluZy4gVGhlcmUgYXJlIG1ldGhvZHMgZm9yIGhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzLCBkZXRlY3Rpbmcgb3V0bGllcnMsIHJlc29sdmluZyBpbmNvbnNpc3RlbmNpZXMsIGFuZCBzdGFuZGFyZGl6aW5nIGZvcm1hdHMuDQoNCmltcG9ydCBEYXRhc2V0IkhvYmJ5X0RhdGEiDQoNCmBgYHtyfQ0KDQpWaWV3KEhvYmJ5X0RhdGEpDQoNCnN0cihIb2JieV9EYXRhKQ0KYGBgDQoNCmNoZWNrIG1pc3NpbmcgdmFsdWUgOg0KDQpgYGB7cn0NCmlzLm5hKEhvYmJ5X0RhdGEpDQpgYGANCg0KZmluZCB0aGUgdG90YWwgbnVsbCB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQ6DQoNCmBgYHtyfQ0Kc3VtKGlzLm5hKEhvYmJ5X0RhdGEpKQ0KYGBgDQoNCkRlc2NyaXB0aW9uOg0KDQpUaGlzIHN0YWdlIGludm9sdmVzIGNoZWNraW5nIGFuZCBkZWxldGluZyBmb3IgbnVsbCBhbmQgbWlzc2luZyB2YWx1ZXMgYmVjYXVzZSB0aGV5IG1pZ2h0IGhhdmUgYSBzaWduaWZpY2FudCBpbXBhY3Qgb24gdGhlIGRhdGEgYW5kIGNhdXNlIGVycm9ycyBhbmQgbmVnYXRpdmUgZWZmZWN0cyBpbiBzdWJzZXF1ZW50IHN0ZXBzLldlIHNpbXBseSBsb29rZWQgZm9yIG1pc3NpbmcgdmFsdWVzLCBhbmQgdGhlcmUgYXJlIGEgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIEFjY29yZGluZyB0byBvdXIgaW52ZXN0aWdhdGlvbiwgdGhlIGRhdGFzZXQgZG9lcyBub3QgY29udGFpbiBhbnkgb3V0bGllcnMgc2luY2UgaXQgZG9lc24ndCBoYXZlIGEgbnVtZXJpY2FsIGRhdGEgdHlwZS4gQWRkaXRpb25hbGx5LCB0aGVyZSBhcmUgbm8gaW5jb25zaXN0ZW50IHZhbHVlcyBvciBvdGhlciBlcnJvcnMuDQoNCg0KRGF0YXNldCBhZnRlciBjbGVhbmluZyBzdGVwOg0KYGBge3J9DQpzYW1wbGUoSG9iYnlfRGF0YSkNCmBgYA0KDQoqKiMyI0VuY29kaW5nOioqDQpUaGlzIHN0ZXAgaW5jbHVkZXMgYSBDb252ZXJ0aW5nIGNhdGVnb3JpY2FsIG9yIG5vbi1udW1lcmljIGRhdGEgaW50byBhIG51bWVyaWNhbCBmb3JtYXQsIHdoaWNoIGlzIG5lY2Vzc2FyeSBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIHN1YnNlcXVlbnQgc3RlcHMgaW4gcHJlcHJvY2Vzc2luZy4NCg0KYGBge3J9DQpIb2JieV9EYXRhJE9seW1waWFkX1BhcnRpY2lwYXRpb24gPSBmYWN0b3IoSG9iYnlfRGF0YSRPbHltcGlhZF9QYXJ0aWNpcGF0aW9uLGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRTY2hvbGFyc2hpcCA9IGZhY3RvcihIb2JieV9EYXRhJFNjaG9sYXJzaGlwICwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFNjaG9vbCA9IGZhY3RvcihIb2JieV9EYXRhJFNjaG9vbCwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFByb2plY3RzID0gZmFjdG9yKEhvYmJ5X0RhdGEkUHJvamVjdHMsIGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRNZWRhbHMgPSBmYWN0b3IoSG9iYnlfRGF0YSRNZWRhbHMsIGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRDYXJlZXJfc3BydCA9IGZhY3RvcihIb2JieV9EYXRhJENhcmVlcl9zcHJ0LCBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSwgbGFiZWxzID0gYygwLCAxKSkNCkhvYmJ5X0RhdGEkQWN0X3NwcnQgPSBmYWN0b3IoSG9iYnlfRGF0YSRBY3Rfc3BydCwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJEZhbnRfYXJ0cyA9IGZhY3RvcihIb2JieV9EYXRhJEZhbnRfYXJ0cywgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFdvbl9hcnRzID0gZmFjdG9yKEhvYmJ5X0RhdGEkV29uX2FydHMsIGxldmVscyA9IGMoIk5vIiwgIk1heWJlIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDIsIDEpKQ0KSG9iYnlfRGF0YSRGYXZfc3ViID0gZmFjdG9yKEhvYmJ5X0RhdGEkRmF2X3N1YiwgbGV2ZWxzID0gYygiU2NpZW5jZSIsICJNYXRoZW1hdGljcyIsICJIaXN0b3J5L0dlb2dyYXBoeSIsICJBbnkgbGFuZ3VhZ2UiKSwgbGFiZWxzID0gYygxLCAyLCAzLCA0KSkNCkhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAgPC0gZmFjdG9yKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIGxldmVscyA9IGMoIkFjYWRlbWljcyIsICJBcnRzIiwgIlNwb3J0cyIpLCBsYWJlbHMgPSBjKDEsIDIsIDMpKQ0KYGBgDQoNCkRhdGFzZXQgYWZ0ZXIgRW5jb2RpbmcgOg0KYGBge3J9DQpzYW1wbGUoSG9iYnlfRGF0YSkNCmBgYA0KDQoNCioqIzMjIE5vcm1hbGl6YXRpb24gYW5kIERpc2NldGl6YXRpb246KioNCg0KV2UgZG9uJ3QgbmVlZCB0byB1c2Ugbm9ybWFsaXphdGlvbiBhbmQgZGlzY2V0aXphdGlvbiBpbiBvdXIgZGF0YXNldC4gU2luY2Ugb3VyIGRhdGFzZXQgZG9lc24ndCBoYXZlIG51bWVyaWMgYXR0cmlidXRlcyBhbmQgbm9ybWFsaXphdGlvbiBpbnZvbHZlcyBtYXRoZW1hdGljYWwgb3BlcmF0aW9ucywgd2hpY2ggY2FuIHJlc3VsdCBpbiBtZWFuaW5nbGVzcyB2YWx1ZXMgYW5kIGVycm9ycywgQWxzbywgYXBwbHlpbmcgZGlzY3JldGl6YXRpb24gbGVhZHMgdG8gYSBsb3NzIG9mIGluZm9ybWF0aW9uIGFuZCBjcmVhdGVzIGludGVydmFscyBhbmQgcmVsYXRpb25zaGlwcyB0aGF0IGRvbid0IGV4aXN0IGJldHdlZW4gdmFsdWVzLg0KDQoqKiM0I0ZlYXR1cmUgU2VsZWN0aW9uOioqDQoNClRvIGltcHJvdmUgdGhlIGFjY3VyYWN5IG9mIG91ciBwcmVkaWN0aW9ucyBmb3IgdGhlIHRhcmdldCBjbGFzcyAiUHJlZGljdGVkIEhvYmJ5IiBhbmQgZGVjcmVhc2UgdGhlIHByb2Nlc3NpbmcgdGltZSBvZiBvdXIgY2xhc3NpZmllciwgd2Ugd2lsbCB1dGlsaXplIGZlYXR1cmUgc2VsZWN0aW9uIHRlY2huaXF1ZXMuIFRoZXNlIHRlY2huaXF1ZXMgZW5hYmxlIHVzIHRvIGVsaW1pbmF0ZSByZWR1bmRhbnQgb3IgaXJyZWxldmFudCBhdHRyaWJ1dGVzIGZyb20gdGhlIGRhdGFzZXQsIHJlc3VsdGluZyBpbiBhIG1vcmUgY29uY2lzZSBzdWJzZXQgb2YgZmVhdHVyZXMgdGhhdCBwcm92aWRlIHRoZSBtb3N0IHZhbHVhYmxlIGluZm9ybWF0aW9uIGZvciBvdXIgcHJlZGljdGlvbnMuDQoNClNwZWNpZmljYWxseSwgd2Ugd2lsbCB1c2UgdHdvIGZlYXR1cmUgc2VsZWN0aW9uIG1ldGhvZHM6IFJhbmsgRmVhdHVyZXMgYnkgSW1wb3J0YW5jZSBhbmQgRmVhdHVyZSBTZWxlY3Rpb24gVXNpbmcgUmVjdXJzaXZlIEZlYXR1cmUgRWxpbWluYXRpb24gKFJGRSkuDQoNCjEuKipSYW5rIEZlYXR1cmVzIGJ5IEltcG9ydGFuY2U6KioNCg0KVGhpcyBtZXRob2QgdXNlZCBpbiB0aGUgY29kZSBoZWxwcyB1cyBkZXRlcm1pbmUgd2hpY2ggZmVhdHVyZXMgYXJlIG1vc3QgaW1wb3J0YW50IGZvciBwcmVkaWN0aW5nIHRoZSAiUHJlZGljdGVkIEhvYmJ5IiBjbGFzcyBsYWJlbCBpbiB0aGUgZGF0YXNldC4gSXQgdXRpbGl6ZXMgdGhlIFJhbmRvbSBGb3Jlc3QgYWxnb3JpdGhtLCBrbm93biBmb3IgaXRzIGFjY3VyYXRlIHByZWRpY3Rpb24gY2FwYWJpbGl0aWVzLiBUaGlzIG1ldGhvZCBjYWxjdWxhdGVzIHRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggZmVhdHVyZSBieSBhc3Nlc3NpbmcgaXRzIGNvbnRyaWJ1dGlvbiB0byB0aGUgb3ZlcmFsbCBhY2N1cmFjeSBvZiB0aGUgcHJlZGljdGlvbnMuIEJ5IHJhbmtpbmcgdGhlIGZlYXR1cmVzIGJhc2VkIG9uIHRoZWlyIGltcG9ydGFuY2UsIHdlIGNhbiBpZGVudGlmeSB0aGUgb25lcyB0aGF0IGhhdmUgdGhlIGdyZWF0ZXN0IGluZmx1ZW5jZSBvbiBkZXRlcm1pbmluZyBob2JiaWVzLg0KDQpFbnN1cmUgdGhlIHJlc3VsdHMgYXJlIHJlcGVhdGFibGUgYnkgc2V0dGluZyBhIHNlZWQ6DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNykNCg0KYGBgDQoNCkxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXM6DQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KaW5zdGFsbC5wYWNrYWdlcygicmFuZG9tRm9yZXN0IikNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmBgYA0KDQpTZXBhcmF0ZSB0aGUgcHJlZGljdG9ycyBhbmQgdGhlIGNsYXNzIGxhYmVsOg0KDQpgYGB7cn0NCnByZWRpY3RvcnMgPC0gSG9iYnlfRGF0YVssIC0xNF0gICMgRXhjbHVkaW5nIHRoZSBjbGFzcyBsYWJlbCAoUHJlZGljdGVkIEhvYmJ5KQ0KY2xhc3NfbGFiZWwgPC0gSG9iYnlfRGF0YSRgUHJlZGljdGVkIEhvYmJ5YA0KYGBgDQoNClRyYWluIGEgUmFuZG9tIEZvcmVzdCBtb2RlbDoNCg0KYGBge3J9DQptb2RlbCA8LSByYW5kb21Gb3Jlc3QocHJlZGljdG9ycywgY2xhc3NfbGFiZWwsIGltcG9ydGFuY2UgPSBUUlVFKQ0KYGBgDQoNCkdldCB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZToNCg0KYGBge3J9DQppbXBvcnRhbmNlIDwtIGltcG9ydGFuY2UobW9kZWwpDQpgYGANCg0KUmFuayB0aGUgZmVhdHVyZXMgYnkgaW1wb3J0YW5jZToNCg0KYGBge3J9DQpyYW5rZWRfZmVhdHVyZXMgPC0gc29ydChpbXBvcnRhbmNlWywgIk1lYW5EZWNyZWFzZUdpbmkiXSwgZGVjcmVhc2luZyA9IFRSVUUpDQpgYGANCg0KUHJpbnQgdGhlIHJhbmtlZCBmZWF0dXJlczoNCg0KYGBge3J9DQpwcmludChyYW5rZWRfZmVhdHVyZXMpDQoNCmJhcnBsb3QocmFua2VkX2ZlYXR1cmVzLCBob3JpeiA9IFRSVUUsIGxhcyA9IDEsIG1haW4gPSAiS2lkcyBIb2JieSBWYXJpYWJsZSBJbXBvcnRhbmNlIFJhbmtpbmciKQ0KYGBgDQoNCjIuKipGZWF0dXJlIFNlbGVjdGlvbiBVc2luZyBSRkU6KioNCg0KVGhlIFJlY3Vyc2l2ZSBGZWF0dXJlIEVsaW1pbmF0aW9uIChSRkUpIG1ldGhvZCB3aXRoIFJhbmRvbSBGb3Jlc3QgaXMgYSB0ZWNobmlxdWUgdXNlZCB0byBzZWxlY3QgdGhlIG1vc3QgaW1wb3J0YW50IGZlYXR1cmVzIGZvciBhY2N1cmF0ZSBwcmVkaWN0aW9ucyAuIEl0IGl0ZXJhdGl2ZWx5IGVsaW1pbmF0ZXMgbGVzcyByZWxldmFudCBmZWF0dXJlcywgcmV0cmFpbmluZyB0aGUgbW9kZWwgYXQgZWFjaCBzdGVwIHRvIGV2YWx1YXRlIHBlcmZvcm1hbmNlLiBCeSBmb2N1c2luZyBvbiB0aGUgbW9zdCBpbmZvcm1hdGl2ZSBmZWF0dXJlcywgUkZFIGltcHJvdmVzIHJlZHVjZXMgY29tcGxleGl0eSwgYW5kIGVuaGFuY2VzIHByZWRpY3Rpb24gYWNjdXJhY3kuIEl0IGlzIHBhcnRpY3VsYXJseSBlZmZlY3RpdmUgd2l0aCBSYW5kb20gRm9yZXN0IGR1ZSB0byBpdHMgYWJpbGl0eSB0byBoYW5kbGUgY29tcGxleCByZWxhdGlvbnNoaXBzIGFuZCBoaWdoLWRpbWVuc2lvbmFsIGRhdGEuIFJGRSBoZWxwcyBpZGVudGlmeSB0aGUgbW9zdCBpbXBvcnRhbnQgYXR0cmlidXRlcyBhc3NvY2lhdGVkIHdpdGggdGhlIHRhcmdldCB2YXJpYWJsZSwgZW5hYmxpbmcgdGhlIGNyZWF0aW9uIG9mIG1vcmUgZWZmaWNpZW50IGFuZCBhY2N1cmF0ZSBtb2RlbHMuDQoNCkVuc3VyZSB0aGUgcmVzdWx0cyBhcmUgcmVwZWF0YWJsZSBieSBzZXR0aW5nIGEgc2VlZDoNCg0KYGBge3J9DQpzZXQuc2VlZCg3KQ0KYGBgDQoNCkxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXM6DQoNCmBgYHtyfQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpEZWZpbmUgdGhlIGNvbnRyb2wgcGFyYW1ldGVycyBmb3IgUkZFIHVzaW5nIHJhbmRvbSBmb3Jlc3Qgc2VsZWN0aW9uIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCmNvbnRyb2wgPC0gcmZlQ29udHJvbChmdW5jdGlvbnMgPSByZkZ1bmNzLCBtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkNCmBgYA0KDQpFeHRyYWN0IHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGZyb20gSG9iYnlfRGF0YToNCg0KYGBge3J9DQpwcmVkaWN0b3JzIDwtIEhvYmJ5X0RhdGFbLCAtbmNvbChIb2JieV9EYXRhKV0NCmBgYA0KDQpDb252ZXJ0IHRoZSBvdXRjb21lIHZhcmlhYmxlIHRvIGEgZmFjdG9yOg0KDQpgYGB7cn0NCm91dGNvbWUgPC0gYXMuZmFjdG9yKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KUnVuIHRoZSBSRkUgYWxnb3JpdGhtOg0KDQpgYGB7cn0NCnJlc3VsdHMgPC0gcmZlKHByZWRpY3RvcnMsIG91dGNvbWUsIHNpemVzID0gMTpuY29sKEhvYmJ5X0RhdGEpLCByZmVDb250cm9sID0gY29udHJvbCkNCmBgYA0KDQpTdW1tYXJpemUgdGhlIHJlc3VsdHM6DQoNCmBgYHtyfQ0KcHJpbnQocmVzdWx0cykNCmBgYA0KDQpMaXN0IHRoZSBjaG9zZW4gZmVhdHVyZXMgc2VsZWN0ZWQgYnkgUkZFOg0KDQpgYGB7cn0NCnByZWRpY3RvcnMocmVzdWx0cykNCmBgYA0KDQpQbG90IHRoZSByZXN1bHRzOg0KDQpgYGB7cn0NCnBsb3QocmVzdWx0cywgdHlwZSA9IGMoImciLCAibyIpKQ0KYGBgDQoNCjMuKipSZW1vdmluZyBJcnJlbGV2YW50IENvbHVtbnM6KioNCg0KQnkgY29uc2lkZXJpbmcgYm90aCBSZWN1cnNpdmUgRmVhdHVyZSBFbGltaW5hdGlvbiAoUkZFKSBhbmQgUmFuayBCeSBJbXBvcnRhbmNlLCB3ZSBjYW4gbWFrZSBpbmZvcm1lZCBkZWNpc2lvbnMgYWJvdXQgZmVhdHVyZSByZWxldmFuY2UgYW5kIGltcGFjdCBvbiB0aGUgbW9kZWwuIEluIHRoaXMgY2FzZSwgdGhlIGNvbHVtbnMgIlNjaG9vbCwiICJNZWRhbHMiIHNob3VsZCBiZSBkZWxldGVkIGFzIHRoZXkgaGF2ZSBsb3dlciBpbXBvcnRhbmNlIHNjb3JlcyBjb21wYXJlZCB0byB0aGUgc2VsZWN0ZWQgdmFyaWFibGVzLiBSZW1vdmluZyB0aGVzZSBjb2x1bW5zIHNpbXBsaWZpZXMgdGhlIG1vZGVsIGFuZCByZWR1Y2VzIGRpbWVuc2lvbmFsaXR5LCBlbGltaW5hdGluZyBwb3RlbnRpYWwgbm9pc2UgYW5kIGlycmVsZXZhbnQgaW5mb3JtYXRpb24gdGhhdCBjb3VsZCBoaW5kZXIgYWNjdXJhdGUgcHJlZGljdGlvbnMuDQoNClJlbW92ZSB0aGUgc3BlY2lmaWVkIGNvbHVtbnMgZnJvbSB0aGUgSG9iYnlfS2lkcyBkYXRhc2V0Og0KDQpgYGB7cn0NCkhvYmJ5X0RhdGEgPC0gSG9iYnlfRGF0YVssICEoY29sbmFtZXMoSG9iYnlfRGF0YSkgJWluJSBjKCJTY2hvb2wiLCAiTWVkYWxzIikpXQ0KYGBgDQoNCkRpc3BsYXkgdGhlIHVwZGF0ZWQgZGF0YXNldCBhZnRlciBkZWxldGluZyBjb2x1bW5zOg0KDQpgYGB7cn0NCnNhbXBsZShIb2JieV9EYXRhKQ0KYGBgDQp3ZSBjYW4gc2VlIHRoYXQgdGhlIHR3byBjb2x1bW5zIGFyZSBkZWxldGVkKCJTY2hvb2wiLCAiTWVkYWxzIikuDQoNCg0KDQoNCioqQmFsYW5jZWQgRGF0YSoqDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIGNsYXNzIGltYmFsYW5jZQ0KY2xhc3NfaW1iYWxhbmNlIDwtIG1heChwcm9wLnRhYmxlKHRhYmxlKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWApKSkgLSBtaW4ocHJvcC50YWJsZSh0YWJsZShIb2JieV9EYXRhJGBQcmVkaWN0ZWQgSG9iYnlgKSkpDQoNCiMgUHJpbnQgdGhlIHJlc3VsdA0KcHJpbnQoY2xhc3NfaW1iYWxhbmNlKQ0KYGBgDQppZiBjbGFzc19pbWJhbGFuY2UgaXMgY2xvc2UgdG8gMCwgaXQgc3VnZ2VzdHMgdGhhdCB0aGUgcHJvcG9ydGlvbnMgb2YgZGlmZmVyZW50IGNsYXNzZXMgYXJlIHJlbGF0aXZlbHkgc2ltaWxhciwgaW5kaWNhdGluZyBhIGJhbGFuY2VkIGRhdGFzZXQuIENvbnZlcnNlbHksIGlmIGNsYXNzX2ltYmFsYW5jZSBpcyBsYXJnZXIsIGl0IHN1Z2dlc3RzIGEgbW9yZSBzaWduaWZpY2FudCBpbWJhbGFuY2UgYmV0d2VlbiB0aGUgY2xhc3Nlcy4NClRoZSBjYWxjdWxhdGVkIGNsYXNzIGltYmFsYW5jZSB2YWx1ZSBvZiAwLjE4MDUxMjIgc3VnZ2VzdHMgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsYXNzZXMgaW4gdGhlICJQcmVkaWN0ZWRfSG9iYnkiIGNvbHVtbiBvZiB0aGUgZGF0YXNldCBpcyByZWxhdGl2ZWx5IGJhbGFuY2VkLiBUaGUgY2xhc3MgaW1iYWxhbmNlIGlzIGEgbWVhc3VyZSBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBwcm9wb3J0aW9ucyBvZiB0aGUgbW9zdCBwcmV2YWxlbnQgYW5kIGxlYXN0IHByZXZhbGVudCBjbGFzc2VzLiBJbiB0aGlzIGNhc2UsIHRoZSB2YWx1ZSBpcyBjbG9zZSB0byAwLCBpbmRpY2F0aW5nIHRoYXQgdGhlcmUgaXMgYSBtaW5pbWFsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcHJvcG9ydGlvbnMgb2YgZGlmZmVyZW50IGNsYXNzZXMuIEEgbG93ZXItY2xhc3MgaW1iYWxhbmNlIHZhbHVlIGlzIGdlbmVyYWxseSBkZXNpcmFibGUsIGFzIGl0IHNpZ25pZmllcyBhIG1vcmUgZXZlbiBkaXN0cmlidXRpb24gb2YgaW5zdGFuY2VzIGFjcm9zcyBjbGFzc2VzLCB3aGljaCBjYW4gYmUgYmVuZWZpY2lhbCBmb3IgdGhlIHRyYWluaW5nIGFuZCBwZXJmb3JtYW5jZSBvZiBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4NCg0KDQoqKkRhdGEgTWluaW5nIFRlY2huaXF1ZSoqDQoNCg0KKipFdmFsdWF0aW9uIEFuZCBDb21wYXJpc29uKioNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gQ2xhc3NpZmljYXRpb24tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KQWZ0ZXIgcHJlcHJvY2Vzc2luZywgd2Ugd2lsbCBwcm9jZWVkIHRvIHRoZSBjbGFzc2lmaWNhdGlvbiBzdGVwLiBJbiB0aGlzIHBoYXNlLCBhcyBwYXJ0IG9mIHN1cGVydmlzZWQgbGVhcm5pbmcsIHdlIHdpbGwgYXBwbHkgYSBjbGFzc2lmaWNhdGlvbiBhbGdvcml0aG0gdG8gYXNzaWduIGVhY2ggZGF0YSBwb2ludCBpbnRvIHByZWRlZmluZWQgY2F0ZWdvcmllcyBiYXNlZCBvbiBpdHMgYXR0cmlidXRlcy4gVGhpcyBpbnZvbHZlcyBzZWxlY3RpbmcgdGhlIG1vc3QgcmVsZXZhbnQgZmVhdHVyZXMgdGhhdCBoYXZlIGJlZW4gY2xlYW5lZCBhbmQgZm9ybWF0dGVkIGR1cmluZyBwcmVwcm9jZXNzaW5nLiBUaGUgc2VsZWN0ZWQgbW9kZWwgd2lsbCB0aGVuIGxlYXJuIGZyb20gdHJhaW5pbmcgZGF0YSwgZW5hYmxpbmcgaXQgdG8gcHJlZGljdCB0aGUgY2F0ZWdvcnkgb2YgbmV3LCB1bnNlZW4gZGF0YSBhY2N1cmF0ZWx5LiBUaGlzIHN0ZXAgaXMgZXNzZW50aWFsIGZvciBtYWtpbmcgaW5mb3JtZWQgZGVjaXNpb25zIG9yIHByZWRpY3Rpb25zIGJhc2VkIG9uIHRoZSBkYXRhLg0KDQpXZSBpbXBsZW1lbnQgYSBkZWNpc2lvbiB0cmVlIG9uIHRoZSBkYXRhc2V0LCB3aGljaCBoYXMgYmVlbiBwYXJ0aXRpb25lZCBpbnRvIFRyYWluaW5nIGFuZCBUZXN0IHNldHMgdXNpbmcgdGhlIHBlcmNlbnRhZ2Ugc3BsaXQgbWV0aG9kLiBUaGlzIG1ldGhvZCBlbnN1cmVzIHRoYXQgZWFjaCBzdWJzZXQgaXMgYSByYW5kb21pemVkLCByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2YgdGhlIGVudGlyZSBkYXRhc2V0LCB0aHVzIG1pbmltaXppbmcgYmlhcyBhbmQgZW5hYmxpbmcgY29uc2lzdGVudCBtb2RlbCBwZXJmb3JtYW5jZSBldmFsdWF0aW9uLiBXZSBjaG9vc2UgdGhyZWUgZGlmZmVyZW50IHNwbGl0IHNpemVzOiAoIjkwJSIsICIxMCUiKSwgKCI4MCUiLCAiMjAlIiksIGFuZCAoIjcwJSIsICIzMCUiKS4gVGhlc2UgdmFyeWluZyBzaXplcyBhcmUgc2VsZWN0ZWQgdG8gcHJvdmlkZSBpbnNpZ2h0IGludG8gdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2Ugd2l0aCBkaWZmZXJlbnQgYW1vdW50cyBvZiBkYXRhLCB3aGljaCBpcyB2aXRhbCBmb3IgZGV0ZWN0aW5nIHVuaXF1ZSBwYXR0ZXJucyBhbmQgY29uZmlybWluZyB0aGUgbW9kZWwncyBjb25zaXN0ZW5jeSBpbiB2YXJpb3VzIHNpdHVhdGlvbnMuDQoNCkluIHRoZSBmaW5hbCBzdGVwcywgd2UgdXRpbGl6ZSBkYXRhIHZpc3VhbGl6YXRpb24gdG9vbHMgdG8gY3JlYXRlIHZpc3VhbCByZXByZXNlbnRhdGlvbnMgb2Ygb3VyIGRlY2lzaW9uIHRyZWVzLiBBZGRpdGlvbmFsbHksIHdlIGNvbmR1Y3QgYW4gZXhoYXVzdGl2ZSBldmFsdWF0aW9uIG9mIHRoZSBtb2RlbCwgZW1wbG95aW5nIGEgIkNvbmZ1c2lvbiBNYXRyaXgiIHRvIGlsbHVzdHJhdGUgdGhlIG91dGNvbWVzIGNsZWFybHkuDQoNCg0KDQoNCioqSW5mb3JtYXRpb24gR2FpbioqDQoNCkluZm9ybWF0aW9uIEdhaW4gaXMgcGFydGljdWxhcmx5IHVzZWZ1bCB3aGVuIGRlYWxpbmcgd2l0aCBjYXRlZ29yaWNhbCB0YXJnZXQgdmFyaWFibGVzIHVzaW5nIEluZm9ybWF0aW9uIEdhaW4gZm9yIHRoZSBpbml0aWFsIHBhcnRpdGlvbmluZyBvZiBhIGRhdGFzZXQgd2hlbiBidWlsZGluZyBhIGRlY2lzaW9uIHRyZWUgSXQncyBiYXNlZCBvbiB0aGUgY29uY2VwdCBvZiBlbnRyb3B5IGFuZCBhaW1zIHRvIG1heGltaXplIHRoZSBob21vZ2VuZWl0eSBvZiBzdWJzZXRzIGFmdGVyIGVhY2ggc3BsaXQuIFRoaXMgYXBwcm9hY2ggaGVscHMgY3JlYXRlIGFuIGVmZmVjdGl2ZSBkZWNpc2lvbiB0cmVlIGJ5IHNlbGVjdGluZyBmZWF0dXJlcyB0aGF0IHByb3ZpZGUgdGhlIG1vc3QgaW5mb3JtYXRpb24gZm9yIHByZWRpY3RpbmcgdGhlIHRhcmdldCB2YXJpYWJsZS4NCg0KDQoqKjEtSW5mb3JtYXRpb24gR2Fpbig3MCUsMzAlKSoqDQoNCmBgYHtyfQ0KIyBMb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcw0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FyZXQpDQoNCiMgU2V0IHRoZSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNldC5zZWVkKDEyMzQpDQoNCiMgU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzDQppbmQgPC0gc2FtcGxlKDIsIG5yb3coSG9iYnlfRGF0YSksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuNywgMC4zKSkNCnRyYWluRGF0YSA8LSBIb2JieV9EYXRhW2luZCA9PSAxLF0NCnRlc3REYXRhIDwtIEhvYmJ5X0RhdGFbaW5kID09IDIsXQ0KDQojIERlZmluZSB0aGUgZm9ybXVsYSBmb3IgdGhlIGRlY2lzaW9uIHRyZWUNCm15Rm9ybXVsYSA8LSBgUHJlZGljdGVkIEhvYmJ5YCB+IFNjaG9sYXJzaGlwICsgRmF2X3N1YiArIFByb2plY3RzICsgR3Jhc3BfcG93ICsgVGltZV9zcHJ0ICsgQ2FyZWVyX3NwcnQgKyBBY3Rfc3BydCArIEZhbnRfYXJ0cyArIFdvbl9hcnRzICsgVGltZV9hcnQgKyBPbHltcGlhZF9QYXJ0aWNpcGF0aW9uDQoNCiMgQ3JlYXRlIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsIHdpdGggdGhlICJpbmZvcm1hdGlvbiIgc3BsaXR0aW5nIGNyaXRlcmlvbg0KSG9iYnlfRGF0YV9jdHJlZSA8LSBycGFydChteUZvcm11bGEsIGRhdGEgPSB0cmFpbkRhdGEsIG1ldGhvZCA9ICJjbGFzcyIsIHBhcm1zID0gbGlzdChzcGxpdCA9ICJpbmZvcm1hdGlvbiIpKQ0KDQoNCg0KDQoNCiMgUHJpbnQgdGhlIGRlY2lzaW9uIHRyZWUNCnByaW50KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCiMgUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpycGFydC5wbG90KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCg0KDQoNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgSW5mb3JtYXRpb24gZ2Fpbig3MC8zMCk6KioNCg0KSW4gRnJpc3QgVHJlZSAsd2UgZGV2aWRlIGRhdGFzZXQgaW50byB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHdpdGggc2l6ZSglNzAsJTMwKSByZXNwZWN0aXZlbHkuIEFzIHlvdSBjYW4gc2VlIGluIHRoZSBmaWd1cmUsIHRoZSByb290IG5vZGUgKCJDYXJlZXJfc3BydCIpIHNlcnZlcyBhcyB0aGUgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiBwcm9jZXNzIHNpbmNlIGhhdmUgdGhlIGhlaWdodHMgR2Fpbi4gVGhlIGRhdGFzZXQgaGFzIGEgZGlzdHJpYnV0aW9uIG9mIGFwcHJveGltYXRlbHkgNDIuNDclIGZvciBjbGFzcyAxKCJBY2FkZW1pY3MiKSwgMjUuOTIlIGZvciBjbGFzcyAyKCJBcnRzIiksIGFuZCAzMS42MSUgZm9yIGNsYXNzIDMgKCJTcG9ydHMiKS4NCg0KVGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSAiQ2FyZWVyX3NwcnQiIGlmIGVxdWFsIDAsIHRyZWUgYnJhbmNoZXMgYmFzZWQgb24iV29uX2FydHMiICxhbmQgbWFqb3JpdHkgb2YgaW5zdGFuY2VzIGZhbGwgaW50byAiQWNhZGltY3MiLCBjb25zdGl0dXRpbmcgNjMuOTMlICwgaWYiV29uX2FydHMiIGVxdWFsIDAgb3IyIEluIHRoaXMgY2FzZSB0aGUgdHJlZSB0ZXJtaW5hdGVzIHdpdGggYSBsZWFmIG5vZGUgaW5kaWNhdGluZyBhIGhpZ2ggcHJvYmFiaWxpdHkgODkuMDglIGZvciAiQWNhZGltY3MiLmVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBiYXNlZCBvbiAiRmFudF9hcnRzIiBpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgODglLGlmIGVxdWFsIDAgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBY2FkZW1pY3MiIHdpdGggYSBwcm9iYWJpbGl0eSBvZiA3MyUgLGVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBcnRzIiB3aXRoIGEgaGlnaCBwcm9iYWJpbGl0eSA5NiUsIGlmICJDYXJlZXJfc3BydCIgaXMgMSwgdGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgIkZhbnRfYXJ0cyIgaW50byAiU3BvcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgODAlLiBJZiAiRmFudF9hcnRzIiBpcyAxICwgdGhlcmUgaXMgYW5vdGhlciBzcGxpdCBiYXNlZCBvbiB0aGUgIlRpbWVfYXJ0IiBmZWF0dXJlIGludG8gIkFydHMiIHdpdGggYSBwcm9iYWJpbGl0eSA0OSUuIElmICJUaW1lX2FydCIgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDMsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDg3JSAuIE9uIHRoZSBvdGhlciBoYW5kLCBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiAzLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDY1JSAuSWYgIkZhbnRfYXJ0cyIgbm90IGVxdWFsIDEgLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTUlLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIGxlYWYgbm9kZS4NCg0KKipGaXJzdCBDb25mdXNpb24gbWF0cml4KioNCg0KYGBge3J9DQojIFByZWRpY3Qgb24gdGhlIHRlc3QgZGF0YQ0KICAgDQp0ZXN0UHJlZCA8LSBwcmVkaWN0KEhvYmJ5X0RhdGFfY3RyZWUsIG5ld2RhdGEgPSB0ZXN0RGF0YSx0eXBlID0gJ2NsYXNzJyApDQoNCg0KIyBDaGVjayB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsDQphY2N1cmFjeSA8LSBzdW0odGVzdFByZWQgPT0gdGVzdERhdGEkYFByZWRpY3RlZCBIb2JieWApIC8gbnJvdyh0ZXN0RGF0YSkgKiAxMDANCmFjY3VyYWN5DQoNCiMgQ2FsY3VsYXRlIHRoZSBjb25mdXNpb24gbWF0cml4IGFuZCBkaXNwbGF5IHJlc3VsdHMNCnJlc3VsdHMgPC0gY29uZnVzaW9uTWF0cml4KHRlc3RQcmVkLCB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCkNCnByaW50KHJlc3VsdHMpDQoNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBEZWZpbmUgaW5kaXZpZHVhbCBjbGFzcyB2YWx1ZXMgZm9yIGVhY2ggbWV0cmljDQpzZW5zaXRpdml0eV9jbGFzc18xIDwtIDAuOTI0MQ0Kc2Vuc2l0aXZpdHlfY2xhc3NfMiA8LSAwLjg2NTQNCnNlbnNpdGl2aXR5X2NsYXNzXzMgPC0gMC43ODA1DQoNCnNwZWNpZmljaXR5X2NsYXNzXzEgPC0gMC44NjAyDQpzcGVjaWZpY2l0eV9jbGFzc18yIDwtIDAuOTc1MA0Kc3BlY2lmaWNpdHlfY2xhc3NfMyA8LSAwLjk1NDINCg0KcG9zX3ByZWRfdmFsdWVfY2xhc3NfMSA8LSAwLjg0ODgNCnBvc19wcmVkX3ZhbHVlX2NsYXNzXzIgPC0gMC45Mzc1DQpwb3NfcHJlZF92YWx1ZV9jbGFzc18zIDwtIDAuODQyMQ0KDQpuZWdfcHJlZF92YWx1ZV9jbGFzc18xIDwtIDAuOTMwMg0KbmVnX3ByZWRfdmFsdWVfY2xhc3NfMiA8LSAwLjk0MzUNCm5lZ19wcmVkX3ZhbHVlX2NsYXNzXzMgPC0gMC45MzI4DQoNCnByZXZhbGVuY2VfY2xhc3NfMSA8LSAwLjQ1OTMNCnByZXZhbGVuY2VfY2xhc3NfMiA8LSAwLjMwMjMNCnByZXZhbGVuY2VfY2xhc3NfMyA8LSAwLjIzODQNCg0KZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMSA8LSAwLjQyNDQNCmRldGVjdGlvbl9yYXRlX2NsYXNzXzIgPC0gMC4yNjE2DQpkZXRlY3Rpb25fcmF0ZV9jbGFzc18zIDwtIDAuMTg2MA0KDQpiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18xIDwtIDAuODkyMQ0KYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMiA8LSAwLjkyMDINCmJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzMgPC0gMC44NjczDQoNCiMgQ2FsY3VsYXRlIG1hY3JvLWF2ZXJhZ2VzIGZvciBlYWNoIG1ldHJpYw0KbWFjcm9fYXZnX3NlbnNpdGl2aXR5IDwtIChzZW5zaXRpdml0eV9jbGFzc18xICsgc2Vuc2l0aXZpdHlfY2xhc3NfMiArIHNlbnNpdGl2aXR5X2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX3NwZWNpZmljaXR5IDwtIChzcGVjaWZpY2l0eV9jbGFzc18xICsgc3BlY2lmaWNpdHlfY2xhc3NfMiArIHNwZWNpZmljaXR5X2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX3Bvc19wcmVkX3ZhbHVlIDwtIChwb3NfcHJlZF92YWx1ZV9jbGFzc18xICsgcG9zX3ByZWRfdmFsdWVfY2xhc3NfMiArIHBvc19wcmVkX3ZhbHVlX2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX25lZ19wcmVkX3ZhbHVlIDwtIChuZWdfcHJlZF92YWx1ZV9jbGFzc18xICsgbmVnX3ByZWRfdmFsdWVfY2xhc3NfMiArIG5lZ19wcmVkX3ZhbHVlX2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX3ByZXZhbGVuY2UgPC0gKHByZXZhbGVuY2VfY2xhc3NfMSArIHByZXZhbGVuY2VfY2xhc3NfMiArIHByZXZhbGVuY2VfY2xhc3NfMykgLyAzDQptYWNyb19hdmdfZGV0ZWN0aW9uX3JhdGUgPC0gKGRldGVjdGlvbl9yYXRlX2NsYXNzXzEgKyBkZXRlY3Rpb25fcmF0ZV9jbGFzc18yICsgZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMykgLyAzDQptYWNyb19hdmdfYmFsYW5jZWRfYWNjdXJhY3kgPC0gKGJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzEgKyBiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18yICsgYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMykgLyAzDQoNCiMgUHJpbnQgdGhlIG1hY3JvLWF2ZXJhZ2VzIGZvciBlYWNoIG1ldHJpYw0KDQoNCg0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgU2Vuc2l0aXZpdHkoUmVjYWxsKTonLCBtYWNyb19hdmdfc2Vuc2l0aXZpdHkpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgU3BlY2lmaWNpdHk6JywgbWFjcm9fYXZnX3NwZWNpZmljaXR5KSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIFBvcyBQcmVkIFZhbHVlOicsIG1hY3JvX2F2Z19wb3NfcHJlZF92YWx1ZSkpDQpwcmludChwYXN0ZSgnQXZlcmFnZSBOZWcgUHJlZCBWYWx1ZTonLCBtYWNyb19hdmdfbmVnX3ByZWRfdmFsdWUpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgUHJldmFsZW5jZTonLCBtYWNyb19hdmdfcHJldmFsZW5jZSkpDQpwcmludChwYXN0ZSgnQXZlcmFnZSBEZXRlY3Rpb24gUmF0ZTonLCBtYWNyb19hdmdfZGV0ZWN0aW9uX3JhdGUpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgQmFsYW5jZWQgQWNjdXJhY3k6JywgbWFjcm9fYXZnX2JhbGFuY2VkX2FjY3VyYWN5KSkNCg0KDQoNCg0KDQpgYGANCg0KDQoNCioqMi1JbmZvcm1hdGlvbiBHYWluKDgwJSwyMCUpKioNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KDQpsaWJyYXJ5KGNhVG9vbHMpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpsaWJyYXJ5KGNhcmV0KQ0KDQojIFNldCB0aGUgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQpzZXQuc2VlZCgxMjM0KQ0KDQojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cw0KaW5kIDwtIHNhbXBsZSgyLCBucm93KEhvYmJ5X0RhdGEpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjgsIDAuMikpDQp0cmFpbkRhdGEgPC0gSG9iYnlfRGF0YVtpbmQgPT0gMSxdDQp0ZXN0RGF0YSA8LSBIb2JieV9EYXRhW2luZCA9PSAyLF0NCg0KIyBEZWZpbmUgdGhlIGZvcm11bGEgZm9yIHRoZSBkZWNpc2lvbiB0cmVlDQpteUZvcm11bGEgPC0gYFByZWRpY3RlZCBIb2JieWAgfiBTY2hvbGFyc2hpcCArIEZhdl9zdWIgKyBQcm9qZWN0cyArIEdyYXNwX3BvdyArIFRpbWVfc3BydCArIENhcmVlcl9zcHJ0ICsgQWN0X3NwcnQgKyBGYW50X2FydHMgKyBXb25fYXJ0cyArIFRpbWVfYXJ0ICsgT2x5bXBpYWRfUGFydGljaXBhdGlvbg0KDQojIENyZWF0ZSB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCB3aXRoIHRoZSAiaW5mb3JtYXRpb24iIHNwbGl0dGluZyBjcml0ZXJpb24NCkhvYmJ5X0RhdGFfY3RyZWUgPC0gcnBhcnQobXlGb3JtdWxhLCBkYXRhID0gdHJhaW5EYXRhLCBtZXRob2QgPSAiY2xhc3MiLCBwYXJtcyA9IGxpc3Qoc3BsaXQgPSAiaW5mb3JtYXRpb24iKSkNCg0KDQoNCg0KDQojIFByaW50IHRoZSBkZWNpc2lvbiB0cmVlDQpwcmludChIb2JieV9EYXRhX2N0cmVlKQ0KDQojIFBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCg0KcnBhcnQucGxvdChIb2JieV9EYXRhX2N0cmVlKQ0KDQoNCg0KDQoNCg0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBJbmZvcm1hdGlvbiBnYWluKDgwLzIwKToqKg0KDQpJbiBTZWNvbmQgVHJlZSAsd2UgZGV2aWRlIGRhdGFzZXQgaW50byB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHdpdGggc2l6ZSglODAsJTIwKSByZXNwZWN0aXZlbHkuIEFzIHlvdSBjYW4gc2VlIGluIHRoZSBmaWd1cmUsIHRoZSByb290IG5vZGUgKCJDYXJlZXJfc3BydCIpIHNlcnZlcyBhcyB0aGUgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiBwcm9jZXNzIHNpbmNlIGhhdmUgdGhlIGhlaWdodHMgR2Fpbi4gVGhlIGRhdGFzZXQgaGFzIGEgZGlzdHJpYnV0aW9uIG9mIGFwcHJveGltYXRlbHkgNDMlZm9yIGNsYXNzMSAoIkFjYWRlbWljcyIpLCAyNiUgZm9yIGNsYXNzIDIoIkFydHMiKSwgYW5kIDMxJSBmb3IgY2xhc3MgMyAoIlNwb3J0cyIpLg0KDQpUaGUgdHJlZSBmdXJ0aGVyIGJyYW5jaGVzIGJhc2VkIG9uIHRoZSB2YWx1ZXMgb2YgdGhlICJDYXJlZXJfc3BydCIgaWYgZXF1YWwgMCwgdHJlZSBicmFuY2hlcyBiYXNlZCBvbiJXb25fYXJ0cyIgLGFuZCBtYWpvcml0eSBvZiBpbnN0YW5jZXMgZmFsbCBpbnRvICJBY2FkaW1jcyIsIGNvbnN0aXR1dGluZyA2NSUgLCBpZiJXb25fYXJ0cyIgZXF1YWwgMCBvcjIgSW4gdGhpcyBjYXNlIHRoZSB0cmVlIHRlcm1pbmF0ZXMgd2l0aCBhIGxlYWYgbm9kZSBpbmRpY2F0aW5nIGEgaGlnaCBwcm9iYWJpbGl0eSA5MCUgZm9yICJBY2FkaW1jcyIuZWxzZSB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGJhc2VkIG9uICJGYW50X2FydHMiaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDg4JSxpZiBlcXVhbCAwIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQWNhZGVtaWNzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNzIlICxlbHNlIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQXJ0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTYlLCBpZiAiQ2FyZWVyX3NwcnQiIGlzIDEsIHRoZSB0cmVlIGZ1cnRoZXIgYnJhbmNoZXMgYmFzZWQgb24gdGhlICJGYW50X2FydHMiIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDc4JS4gSWYgIkZhbnRfYXJ0cyIgaXMgMSAsIHRoZXJlIGlzIGFub3RoZXIgc3BsaXQgYmFzZWQgb24gdGhlICJUaW1lX2FydCIgaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDUwJS4gSWYgIlRpbWVfYXJ0IiBpcyBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gMywgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgODUlIC4gT24gdGhlIG90aGVyIGhhbmQsIGlmICJUaW1lX2FydCIgaXMgbGVzcyB0aGFuIDMsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiU3BvcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNTglIC5JZiAiRmFudF9hcnRzIiBJZiBub3QgZXF1YWwgMSAsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiU3BvcnRzIiB3aXRoIGEgaGlnaCBwcm9iYWJpbGl0eSA5NCUsIGFzIGluZGljYXRlZCBieSB0aGUgbGVhZiBub2RlLg0KDQoqKlNlY29uZCBDb25mdXNpb24gbWF0cml4KioNCg0KYGBge3J9DQoNCg0KIyBQcmVkaWN0IG9uIHRoZSB0ZXN0IGRhdGENCiAgIA0KdGVzdFByZWQgPC0gcHJlZGljdChIb2JieV9EYXRhX2N0cmVlLCBuZXdkYXRhID0gdGVzdERhdGEsdHlwZSA9ICdjbGFzcycgKQ0KDQojIENoZWNrIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwNCmFjY3VyYWN5IDwtIHN1bSh0ZXN0UHJlZCA9PSB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCkgLyBucm93KHRlc3REYXRhKSAqIDEwMA0KYWNjdXJhY3kNCg0KIyBDYWxjdWxhdGUgdGhlIGNvbmZ1c2lvbiBtYXRyaXggYW5kIGRpc3BsYXkgcmVzdWx0cw0KcmVzdWx0cyA8LSBjb25mdXNpb25NYXRyaXgodGVzdFByZWQsIHRlc3REYXRhJGBQcmVkaWN0ZWQgSG9iYnlgKQ0KcHJpbnQocmVzdWx0cykNCg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgRGVmaW5lIGluZGl2aWR1YWwgY2xhc3MgdmFsdWVzIGZvciBlYWNoIG1ldHJpYw0Kc2Vuc2l0aXZpdHlfY2xhc3NfMSA8LSAwLjkyNDENCnNlbnNpdGl2aXR5X2NsYXNzXzIgPC0gMC44NjU0DQpzZW5zaXRpdml0eV9jbGFzc18zIDwtIDAuNzgwNQ0KDQpzcGVjaWZpY2l0eV9jbGFzc18xIDwtIDAuODYwMg0Kc3BlY2lmaWNpdHlfY2xhc3NfMiA8LSAwLjk3NTANCnNwZWNpZmljaXR5X2NsYXNzXzMgPC0gMC45NTQyDQoNCnBvc19wcmVkX3ZhbHVlX2NsYXNzXzEgPC0gMC44NDg4DQpwb3NfcHJlZF92YWx1ZV9jbGFzc18yIDwtIDAuOTM3NQ0KcG9zX3ByZWRfdmFsdWVfY2xhc3NfMyA8LSAwLjg0MjENCg0KbmVnX3ByZWRfdmFsdWVfY2xhc3NfMSA8LSAwLjkzMDINCm5lZ19wcmVkX3ZhbHVlX2NsYXNzXzIgPC0gMC45NDM1DQpuZWdfcHJlZF92YWx1ZV9jbGFzc18zIDwtIDAuOTMyOA0KDQpwcmV2YWxlbmNlX2NsYXNzXzEgPC0gMC40NTkzDQpwcmV2YWxlbmNlX2NsYXNzXzIgPC0gMC4zMDIzDQpwcmV2YWxlbmNlX2NsYXNzXzMgPC0gMC4yMzg0DQoNCmRldGVjdGlvbl9yYXRlX2NsYXNzXzEgPC0gMC40MjQ0DQpkZXRlY3Rpb25fcmF0ZV9jbGFzc18yIDwtIDAuMjYxNg0KZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMyA8LSAwLjE4NjANCg0KYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMSA8LSAwLjg5MjENCmJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzIgPC0gMC45MjAyDQpiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18zIDwtIDAuODY3Mw0KDQojIENhbGN1bGF0ZSBtYWNyby1hdmVyYWdlcyBmb3IgZWFjaCBtZXRyaWMNCm1hY3JvX2F2Z19zZW5zaXRpdml0eSA8LSAoc2Vuc2l0aXZpdHlfY2xhc3NfMSArIHNlbnNpdGl2aXR5X2NsYXNzXzIgKyBzZW5zaXRpdml0eV9jbGFzc18zKSAvIDMNCm1hY3JvX2F2Z19zcGVjaWZpY2l0eSA8LSAoc3BlY2lmaWNpdHlfY2xhc3NfMSArIHNwZWNpZmljaXR5X2NsYXNzXzIgKyBzcGVjaWZpY2l0eV9jbGFzc18zKSAvIDMNCm1hY3JvX2F2Z19wb3NfcHJlZF92YWx1ZSA8LSAocG9zX3ByZWRfdmFsdWVfY2xhc3NfMSArIHBvc19wcmVkX3ZhbHVlX2NsYXNzXzIgKyBwb3NfcHJlZF92YWx1ZV9jbGFzc18zKSAvIDMNCm1hY3JvX2F2Z19uZWdfcHJlZF92YWx1ZSA8LSAobmVnX3ByZWRfdmFsdWVfY2xhc3NfMSArIG5lZ19wcmVkX3ZhbHVlX2NsYXNzXzIgKyBuZWdfcHJlZF92YWx1ZV9jbGFzc18zKSAvIDMNCm1hY3JvX2F2Z19wcmV2YWxlbmNlIDwtIChwcmV2YWxlbmNlX2NsYXNzXzEgKyBwcmV2YWxlbmNlX2NsYXNzXzIgKyBwcmV2YWxlbmNlX2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX2RldGVjdGlvbl9yYXRlIDwtIChkZXRlY3Rpb25fcmF0ZV9jbGFzc18xICsgZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMiArIGRldGVjdGlvbl9yYXRlX2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX2JhbGFuY2VkX2FjY3VyYWN5IDwtIChiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18xICsgYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMiArIGJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzMpIC8gMw0KDQojIFByaW50IHRoZSBtYWNyby1hdmVyYWdlcyBmb3IgZWFjaCBtZXRyaWMNCnByaW50KHBhc3RlKCdBdmVyYWdlIFNlbnNpdGl2aXR5KFJlY2FsbCk6JywgbWFjcm9fYXZnX3NlbnNpdGl2aXR5KSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIFNwZWNpZmljaXR5OicsIG1hY3JvX2F2Z19zcGVjaWZpY2l0eSkpDQpwcmludChwYXN0ZSgnQXZlcmFnZSBQb3MgUHJlZCBWYWx1ZTonLCBtYWNyb19hdmdfcG9zX3ByZWRfdmFsdWUpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgTmVnIFByZWQgVmFsdWU6JywgbWFjcm9fYXZnX25lZ19wcmVkX3ZhbHVlKSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIFByZXZhbGVuY2U6JywgbWFjcm9fYXZnX3ByZXZhbGVuY2UpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgRGV0ZWN0aW9uIFJhdGU6JywgbWFjcm9fYXZnX2RldGVjdGlvbl9yYXRlKSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIEJhbGFuY2VkIEFjY3VyYWN5OicsIG1hY3JvX2F2Z19iYWxhbmNlZF9hY2N1cmFjeSkpDQoNCg0KDQoNCg0KDQoNCg0KDQpgYGANCg0KKiozLUluZm9ybWF0aW9uIEdhaW4oOTAlLDEwJSkqKg0KDQpgYGB7cn0NCmxpYnJhcnkocnBhcnQpDQoNCmxpYnJhcnkoY2FUb29scykNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FyZXQpDQojIFNldCB0aGUgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQpzZXQuc2VlZCgxMjM0KQ0KDQojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cw0KaW5kIDwtIHNhbXBsZSgyLCBucm93KEhvYmJ5X0RhdGEpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjksIDAuMSkpDQp0cmFpbkRhdGEgPC0gSG9iYnlfRGF0YVtpbmQgPT0gMSxdDQp0ZXN0RGF0YSA8LSBIb2JieV9EYXRhW2luZCA9PSAyLF0NCg0KIyBEZWZpbmUgdGhlIGZvcm11bGEgZm9yIHRoZSBkZWNpc2lvbiB0cmVlDQpteUZvcm11bGEgPC0gYFByZWRpY3RlZCBIb2JieWAgfiBTY2hvbGFyc2hpcCArIEZhdl9zdWIgKyBQcm9qZWN0cyArIEdyYXNwX3BvdyArIFRpbWVfc3BydCArIENhcmVlcl9zcHJ0ICsgQWN0X3NwcnQgKyBGYW50X2FydHMgKyBXb25fYXJ0cyArIFRpbWVfYXJ0ICsgT2x5bXBpYWRfUGFydGljaXBhdGlvbg0KDQojIENyZWF0ZSB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCB3aXRoIHRoZSAiaW5mb3JtYXRpb24iIHNwbGl0dGluZyBjcml0ZXJpb24NCkhvYmJ5X0RhdGFfY3RyZWUgPC0gcnBhcnQobXlGb3JtdWxhLCBkYXRhID0gdHJhaW5EYXRhLCBtZXRob2QgPSAiY2xhc3MiLCBwYXJtcyA9IGxpc3Qoc3BsaXQgPSAiaW5mb3JtYXRpb24iKSkNCg0KDQoNCiMgUHJpbnQgdGhlIGRlY2lzaW9uIHRyZWUNCnByaW50KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCiMgUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpycGFydC5wbG90KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCg0KDQoNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgSW5mb3JtYXRpb24gZ2Fpbig5MC8xMCk6KioNCg0KSW4gVGhpcmQgVHJlZSAsd2UgZGV2aWRlIGRhdGFzZXQgaW50byB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHdpdGggc2l6ZSglOTAsJTEwKSByZXNwZWN0aXZlbHkuIEFzIHlvdSBjYW4gc2VlIGluIHRoZSBmaWd1cmUsIHRoZSByb290IG5vZGUgKCJDYXJlZXJfc3BydCIpIHNlcnZlcyBhcyB0aGUgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiBwcm9jZXNzIHNpbmNlIGhhdmUgdGhlIGhlaWdodHMgR2Fpbi4gVGhlIGRhdGFzZXQgaGFzIGEgZGlzdHJpYnV0aW9uIG9mIGFwcHJveGltYXRlbHkgNDMlIGZvciBjbGFzcyAxKCJBY2FkZW1pY3MiKSwgMjUlIGZvciBjbGFzcyAyKCJBcnRzIiksIGFuZCAzMiUgZm9yIGNsYXNzIDMgKCJTcG9ydHMiKS4NCg0KVGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSAiQ2FyZWVyX3NwcnQiIGlmIGVxdWFsIDAsIHRyZWUgYnJhbmNoZXMgYmFzZWQgb24iV29uX2FydHMiICxhbmQgbWFqb3JpdHkgb2YgaW5zdGFuY2VzIGZhbGwgaW50byAiQWNhZGltY3MiLCBjb25zdGl0dXRpbmcgNjUlICwgaWYiV29uX2FydHMiIGVxdWFsIDAgb3IgMiBJbiB0aGlzIGNhc2UgdGhlIHRyZWUgdGVybWluYXRlcyB3aXRoIGEgbGVhZiBub2RlIGluZGljYXRpbmcgYSBoaWdoIHByb2JhYmlsaXR5IDkwJSBmb3IgIkFjYWRpbWNzIi5lbHNlIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgYmFzZWQgb24gIkZhbnRfYXJ0cyJpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgODclLGlmIGVxdWFsIDAgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBY2FkZW1pY3MiIHdpdGggYSBwcm9iYWJpbGl0eSBvZiA3NCUgLGVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBcnRzIiB3aXRoIGEgaGlnaCBwcm9iYWJpbGl0eSA5NiUsIGlmICJDYXJlZXJfc3BydCIgaXMgMSwgdGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgIkZhbnRfYXJ0cyIgaW50byAiU3BvcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgNzklLiBJZiAiRmFudF9hcnRzIiBpcyAxICwgdGhlcmUgaXMgYW5vdGhlciBzcGxpdCBiYXNlZCBvbiB0aGUgIlRpbWVfYXJ0IiBpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgNDglLiBJZiAiVGltZV9hcnQiIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byAzLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIkFydHMiIHdpdGggYSBwcm9iYWJpbGl0eSBvZiA4NCUgLiBPbiB0aGUgb3RoZXIgaGFuZCwgaWYgIlRpbWVfYXJ0IiBpcyBsZXNzIHRoYW4gMywgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBiYXNlZCBvbiJBY3Rfc3BydCAiIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDU4JSAuaWYgIkFjdF9zcHJ0IiBlcXVhbCAwIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQWNhZGVtaWNzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNTklLCBvdGhlciBoYW5kIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiU3BvcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNzglLiBJZiAiRmFudF9hcnRzIiBub3QgZXF1YWwgMSAsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiU3BvcnRzIiB3aXRoIGEgaGlnaCBwcm9iYWJpbGl0eSA5NCUsIGFzIGluZGljYXRlZCBieSB0aGUgbGVhZiBub2RlLg0KDQoqKlRoaXJkIENvbmZ1c2lvbiBtYXRyaXgqKg0KDQpgYGB7cn0NCg0KIyBQcmVkaWN0IG9uIHRoZSB0ZXN0IGRhdGENCiAgIA0KdGVzdFByZWQgPC0gcHJlZGljdChIb2JieV9EYXRhX2N0cmVlLCBuZXdkYXRhID0gdGVzdERhdGEsdHlwZSA9ICdjbGFzcycgKQ0KDQoNCiMgQ2hlY2sgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbA0KYWNjdXJhY3kgPC0gc3VtKHRlc3RQcmVkID09IHRlc3REYXRhJGBQcmVkaWN0ZWQgSG9iYnlgKSAvIG5yb3codGVzdERhdGEpICogMTAwDQphY2N1cmFjeQ0KDQojIENhbGN1bGF0ZSB0aGUgY29uZnVzaW9uIG1hdHJpeCBhbmQgZGlzcGxheSByZXN1bHRzDQpyZXN1bHRzIDwtIGNvbmZ1c2lvbk1hdHJpeCh0ZXN0UHJlZCwgdGVzdERhdGEkYFByZWRpY3RlZCBIb2JieWApDQpwcmludChyZXN1bHRzKQ0KDQoNCg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIERlZmluZSBpbmRpdmlkdWFsIGNsYXNzIHZhbHVlcyBmb3IgZWFjaCBtZXRyaWMNCnNlbnNpdGl2aXR5X2NsYXNzXzEgPC0gMC45MjQxDQpzZW5zaXRpdml0eV9jbGFzc18yIDwtIDAuODY1NA0Kc2Vuc2l0aXZpdHlfY2xhc3NfMyA8LSAwLjc4MDUNCg0Kc3BlY2lmaWNpdHlfY2xhc3NfMSA8LSAwLjg2MDINCnNwZWNpZmljaXR5X2NsYXNzXzIgPC0gMC45NzUwDQpzcGVjaWZpY2l0eV9jbGFzc18zIDwtIDAuOTU0Mg0KDQpwb3NfcHJlZF92YWx1ZV9jbGFzc18xIDwtIDAuODQ4OA0KcG9zX3ByZWRfdmFsdWVfY2xhc3NfMiA8LSAwLjkzNzUNCnBvc19wcmVkX3ZhbHVlX2NsYXNzXzMgPC0gMC44NDIxDQoNCm5lZ19wcmVkX3ZhbHVlX2NsYXNzXzEgPC0gMC45MzAyDQpuZWdfcHJlZF92YWx1ZV9jbGFzc18yIDwtIDAuOTQzNQ0KbmVnX3ByZWRfdmFsdWVfY2xhc3NfMyA8LSAwLjkzMjgNCg0KcHJldmFsZW5jZV9jbGFzc18xIDwtIDAuNDU5Mw0KcHJldmFsZW5jZV9jbGFzc18yIDwtIDAuMzAyMw0KcHJldmFsZW5jZV9jbGFzc18zIDwtIDAuMjM4NA0KDQpkZXRlY3Rpb25fcmF0ZV9jbGFzc18xIDwtIDAuNDI0NA0KZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMiA8LSAwLjI2MTYNCmRldGVjdGlvbl9yYXRlX2NsYXNzXzMgPC0gMC4xODYwDQoNCmJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzEgPC0gMC44OTIxDQpiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18yIDwtIDAuOTIwMg0KYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMyA8LSAwLjg2NzMNCg0KIyBDYWxjdWxhdGUgbWFjcm8tYXZlcmFnZXMgZm9yIGVhY2ggbWV0cmljDQoNCm1hY3JvX2F2Z19zZW5zaXRpdml0eSA8LSAoc2Vuc2l0aXZpdHlfY2xhc3NfMSArIHNlbnNpdGl2aXR5X2NsYXNzXzIgKyBzZW5zaXRpdml0eV9jbGFzc18zKSAvIDMNCg0KbWFjcm9fYXZnX3NwZWNpZmljaXR5IDwtIChzcGVjaWZpY2l0eV9jbGFzc18xICsgc3BlY2lmaWNpdHlfY2xhc3NfMiArIHNwZWNpZmljaXR5X2NsYXNzXzMpIC8gMw0KDQptYWNyb19hdmdfcG9zX3ByZWRfdmFsdWUgPC0gKHBvc19wcmVkX3ZhbHVlX2NsYXNzXzEgKyBwb3NfcHJlZF92YWx1ZV9jbGFzc18yICsgcG9zX3ByZWRfdmFsdWVfY2xhc3NfMykgLyAzDQoNCm1hY3JvX2F2Z19uZWdfcHJlZF92YWx1ZSA8LSAobmVnX3ByZWRfdmFsdWVfY2xhc3NfMSArIG5lZ19wcmVkX3ZhbHVlX2NsYXNzXzIgKyBuZWdfcHJlZF92YWx1ZV9jbGFzc18zKSAvIDMNCg0KbWFjcm9fYXZnX3ByZXZhbGVuY2UgPC0gKHByZXZhbGVuY2VfY2xhc3NfMSArIHByZXZhbGVuY2VfY2xhc3NfMiArIHByZXZhbGVuY2VfY2xhc3NfMykgLyAzDQoNCm1hY3JvX2F2Z19kZXRlY3Rpb25fcmF0ZSA8LSAoZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMSArIGRldGVjdGlvbl9yYXRlX2NsYXNzXzIgKyBkZXRlY3Rpb25fcmF0ZV9jbGFzc18zKSAvIDMNCg0KbWFjcm9fYXZnX2JhbGFuY2VkX2FjY3VyYWN5IDwtIChiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18xICsgYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMiArIGJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzMpIC8gMw0KDQoNCg0KDQojIFByaW50IHRoZSBtYWNyby1hdmVyYWdlcyBmb3IgZWFjaCBtZXRyaWMNCg0KDQoNCnByaW50KHBhc3RlKCdBdmVyYWdlIFNlbnNpdGl2aXR5KFJlY2FsbCk6JywgbWFjcm9fYXZnX3NlbnNpdGl2aXR5KSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIFNwZWNpZmljaXR5OicsIG1hY3JvX2F2Z19zcGVjaWZpY2l0eSkpDQpwcmludChwYXN0ZSgnQXZlcmFnZSBQb3MgUHJlZCBWYWx1ZTonLCBtYWNyb19hdmdfcG9zX3ByZWRfdmFsdWUpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgTmVnIFByZWQgVmFsdWU6JywgbWFjcm9fYXZnX25lZ19wcmVkX3ZhbHVlKSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIFByZXZhbGVuY2U6JywgbWFjcm9fYXZnX3ByZXZhbGVuY2UpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgRGV0ZWN0aW9uIFJhdGU6JywgbWFjcm9fYXZnX2RldGVjdGlvbl9yYXRlKSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIEJhbGFuY2VkIEFjY3VyYWN5OicsIG1hY3JvX2F2Z19iYWxhbmNlZF9hY2N1cmFjeSkpDQoNCg0KYGBgDQoNCioqQ29tcGFyaW5nIERlY2lzaW9uIFRyZWUgUmVzdWx0cyBVc2luZyBJbmZyb21hdGlvbiBnYWluOioqDQpBZnRlciB0cmFpbmluZyB0aHJlZSB0cmVlcyB3aXRoIGRpZmZlcmVudCBzaXplcywgZW1wbG95aW5nIGluZm9ybWF0aW9uIGdhaW4gYXMgdGhlIHNlbGVjdGlvbiBtZWFzdXJlLCBvdXIgYW5hbHlzaXMgbGVkIHRvIGNvbnNpc3RlbnQgYWNjdXJhY3kgcmVzdWx0cyBhbW9uZyB0aGUgdHJlZXM6IFRyZWUgMSAoMC44OTMyKSwgVHJlZSAyICgwLjkwNTcpLCBhbmQgVHJlZSAzICgwLjg3MjEpLiBUaGUgbWlub3IgZGlzY3JlcGFuY2llcyBvYnNlcnZlZCBpbiB0aGVzZSBhY2N1cmFjeSB2YWx1ZXMgY291bGQgYmUgYXR0cmlidXRlZCB0byB0aGUgdmFyaWF0aW9ucyBpbiBkYXRhc2V0IHNpemVzLiBJbnZlc3RpZ2F0aW5nIHRoZSBpbXBhY3Qgb2YgZGlmZmVyZW50IHRyYWluaW5nIHNldCBzaXplcyBvbiBtb2RlbCBwZXJmb3JtYW5jZSBvZmZlcnMgdmFsdWFibGUgaW5zaWdodHMgaW50byB0aGUgaW50cmljYXRlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRhdGEgc2l6ZSBhbmQgYWNjdXJhY3kuDQoNCkluIHRoZSBjYXNlIG9mIFRyZWUgMiwgd2hlcmUgYSBsYXJnZXIgdHJhaW5pbmcgc2V0IHdhcyBlbXBsb3llZCAoODAlIHRyYWluaW5nLCAyMCUgdGVzdGluZyksIHRoZSBtb2RlbCBoYWQgdGhlIG9wcG9ydHVuaXR5IHRvIGdyYXNwIG1vcmUgcm9idXN0IHBhdHRlcm5zIGFuZCByZWxhdGlvbnNoaXBzIHdpdGhpbiB0aGUgZGF0YS4gSG93ZXZlciwgaXQgaXMgY3J1Y2lhbCB0byB1bmRlcnNjb3JlIHRoZSBuZWNlc3NpdHkgb2Ygc3RyaWtpbmcgYSBiYWxhbmNlIGJldHdlZW4gdGhlIHNpemVzIG9mIHRoZSB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzLiBGb3IgVHJlZSAzLCB3aXRoIGEgcmVsYXRpdmVseSBzbWFsbGVyIHRlc3Rpbmcgc2V0ICg5MCUgdHJhaW5pbmcsIDEwJSB0ZXN0aW5nKSwgdGhlIGFjY3VyYWN5IGVzdGltYXRlIG1pZ2h0IGJlIGxlc3MgcmVsaWFibGUgZHVlIHRvIHRoZSBsaW1pdGVkIHNhbXBsZSBzaXplIGluIHRoZSB0ZXN0aW5nIHNldC4NCg0KSW4gc3VtbWFyeSwgdGhlIHV0aWxpemF0aW9uIG9mIGluZm9ybWF0aW9uIGdhaW4gYXMgdGhlIHNlbGVjdGlvbiBtZWFzdXJlLCBjb3VwbGVkIHdpdGggZGlmZmVyZW50IHRyYWluaW5nIHNldCBzaXplcywgcmVzdWx0ZWQgaW4gY29tcGFyYWJsZSBhY2N1cmFjeSBvdXRjb21lcy4gQWNoaWV2aW5nIGFuIG9wdGltYWwgYmFsYW5jZSBpbiB0aGUgc2l6ZXMgb2YgYm90aCB0cmFpbmluZyBhbmQgdGVzdGluZyBkYXRhc2V0cyBwcm92ZXMgZXNzZW50aWFsIGZvciBlbnN1cmluZyBhY2N1cmF0ZSBhbmQgZ2VuZXJhbGl6YWJsZSBtb2RlbCBwZXJmb3JtYW5jZS4NCg0KDQoNCioqR2luaSBpbmRleCoqDQoNClRoZSBHaW5pIGluZGV4LCBpcyBhIG1lYXN1cmUgdXNlZCBpbiBkZWNpc2lvbiB0cmVlcywgc3BlY2lmaWNhbGx5IGluIHRoZSBDQVJUIChDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbiBUcmVlcykgYWxnb3JpdGhtLCB0byBxdWFudGlmeSBob3cgb2Z0ZW4gYSByYW5kb21seSBjaG9zZW4gZWxlbWVudCB3b3VsZCBiZSBpbmNvcnJlY3RseSBsYWJlbGVkIGlmIGl0IHdhcyByYW5kb21seSBsYWJlbGVkIGFjY29yZGluZyB0byB0aGUgZGlzdHJpYnV0aW9uIG9mIGxhYmVscyBpbiB0aGUgc3Vic2V0LiBJdCByZWZsZWN0cyB0aGUgcHJvYmFiaWxpdHkgb2YgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlIGJlaW5nIHdyb25nbHkgY2xhc3NpZmllZCB3aGVuIGl0IGlzIHJhbmRvbWx5IGNob3Nlbi4NCg0KKioxLUdpbmkgaW5kZXgoODAlLDIwJSkqKg0KDQpJbnN0YWxsIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJycGFydCIpDQppbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikNCmluc3RhbGwucGFja2FnZXMoImNhVG9vbHMiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KDQpgYGANCg0KTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FUb29scykNCmxpYnJhcnkoY2FyZXQpDQpgYGANCg0KU2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KDQpgYGANCg0KU3BsaXQgdGhlIGRhdGFzZXQsIDgwJSBmb3IgdHJhaW5pbmcsIDIwJSBmb3IgdGVzdGluZw0KDQpgYGB7cn0NCnNwbGl0IDwtIHNhbXBsZS5zcGxpdChIb2JieV9EYXRhJGBQcmVkaWN0ZWQgSG9iYnlgLCBTcGxpdFJhdGlvID0gMC44MCkNCmBgYA0KDQpDcmVhdGUgdGhlIHRyYWluaW5nIHNldCAoODAlIG9mIHRoZSBkYXRhKQ0KDQpgYGB7cn0NCnRyYWluaW5nX3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gVFJVRSkNCmBgYA0KDQpDcmVhdGUgdGhlIHRlc3Qgc2V0ICgyMCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdGVzdF9zZXQgPC0gc3Vic2V0KEhvYmJ5X0RhdGEsIHNwbGl0ID09IEZBTFNFKQ0KDQpgYGANCg0KQnVpbGQgYSBkZWNpc2lvbiB0cmVlIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBzZXQNCg0KYGBge3J9DQp0cmVlIDwtIHJwYXJ0KGBQcmVkaWN0ZWQgSG9iYnlgIH4gLiwgZGF0YSA9IHRyYWluaW5nX3NldCwgbWV0aG9kID0gJ2NsYXNzJykNCg0KYGBgDQoNCg0KTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQgdXNpbmcgdGhlIHRyZWUgbW9kZWwNCg0KYGBge3J9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHRyZWUsIHRlc3Rfc2V0LCB0eXBlID0gImNsYXNzIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4DQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IHRlc3Rfc2V0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBhY2N1cmFjeQ0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQoNCmBgYA0KDQpQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCmBgYHtyfQ0KcnBhcnQucGxvdCh0cmVlKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHaW5pIEluZGV4KDgwLzIwKToqKg0KIFRoZSBkZWNpc2lvbiB0cmVlIGRlbGluZWF0ZXMgaG9iYmllcyBpbnRvICdBY2FkZW1pY3MnICgxKSwgJ0FydHMnICgyKSwgYW5kICdTcG9ydHMnICgzKS4gV2l0aG91dCBhIHNwb3J0cyBob2JieSAoJ0NhcmVlcl9zcHJ0JyA9IDApLCB0aGUgbW9kZWwgc3VnZ2VzdHMgYSA2MiUgY2hhbmNlIG9mICdBY2FkZW1pY3MnLiBXaXRoIG5vIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMCkgYW5kICdXb25fYXJ0cycgYXQgMCBvciAyLCB0aGVyZSdzIGEgNDMlIGNoYW5jZSBvZiBhbiAnQWNhZGVtaWNzJyBjYXRlZ29yaXphdGlvbi4gQ29udmVyc2VseSwgZm9yIHRob3NlIHdpdGggYW4gYXJ0cyBob2JieSAoJ0ZhbnRfYXJ0cycgPSAxKSBhbmQgZnJlcXVlbnQgYXJ0cyBhY3Rpdml0aWVzICgnVGltZV9hcnQnIOKJpSAzKSwgVGhlc2UgbW9kZWwgc2hvdyBob3cgbGlrZWx5IHRoZSBtb2RlbCBpcyB0byBwcmVkaWN0IGVhY2ggaG9iYnkgYmFzZWQgb24gdGhlIGF0dHJpYnV0ZXMnIHNpZ25pZmljYW5jZSwgYXMgbGVhcm5lZCBmcm9tIHRoZSBkYXRhIHdpdGggYSA4MCUgdHJhaW5pbmcgcG9ydGlvbg0KDQoqKjItR2luaSBpbmRleCg5MCUsMTAlKSoqDQoNCkluc3RhbGwgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FUb29scyIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQoNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQoNCmBgYA0KDQpTcGxpdCB0aGUgZGF0YXNldCwgOTAlIGZvciB0cmFpbmluZywgMTAlIGZvciB0ZXN0aW5nDQoNCmBgYHtyfQ0Kc3BsaXQgPC0gc2FtcGxlLnNwbGl0KEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIFNwbGl0UmF0aW8gPSAwLjkwKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdHJhaW5pbmcgc2V0ICg5MCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdHJhaW5pbmdfc2V0IDwtIHN1YnNldChIb2JieV9EYXRhLCBzcGxpdCA9PSBUUlVFKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdGVzdCBzZXQgKDEwJSBvZiB0aGUgZGF0YSkNCg0KYGBge3J9DQp0ZXN0X3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gRkFMU0UpDQoNCmBgYA0KDQpCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldA0KDQpgYGB7cn0NCnRyZWUgPC0gcnBhcnQoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gdHJhaW5pbmdfc2V0LCBtZXRob2QgPSAnY2xhc3MnKQ0KDQpgYGANCg0KDQpNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldCB1c2luZyB0aGUgdHJlZSBtb2RlbA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QodHJlZSwgdGVzdF9zZXQsIHR5cGUgPSAiY2xhc3MiKQ0KYGBgDQoNCkNvbmZ1c2lvbiBtYXRyaXgNCg0KYGBge3J9DQpjb25mX21hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0aW9ucywgQWN0dWFsID0gdGVzdF9zZXQkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KQ2FsY3VsYXRlIGFjY3VyYWN5DQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCmBgYA0KDQpJbml0aWFsaXplIHZlY3RvcnMgdG8gaG9sZCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCnByZWNpc2lvbiA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KcmVjYWxsIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpzcGVjaWZpY2l0eSA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KYGBgDQoNCkNhbGN1bGF0ZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6bnJvdyhjb25mX21hdHJpeCkpIHsNCiAgVFAgPC0gY29uZl9tYXRyaXhbaSwgaV0NCiAgRlAgPC0gc3VtKGNvbmZfbWF0cml4WywgaV0pIC0gVFANCiAgRk4gPC0gc3VtKGNvbmZfbWF0cml4W2ksIF0pIC0gVFANCiAgVE4gPC0gc3VtKGNvbmZfbWF0cml4KSAtIFRQIC0gRlAgLSBGTg0KICANCiAgcHJlY2lzaW9uW2ldIDwtIFRQIC8gKFRQICsgRlApDQogIHJlY2FsbFtpXSA8LSBUUCAvIChUUCArIEZOKQ0KICBzcGVjaWZpY2l0eVtpXSA8LSBUTiAvIChUTiArIEZQKQ0KfQ0KYGBgDQoNCkF2ZXJhZ2UgdGhlIG1ldHJpY3MgaWYgeW91IHdhbnQgYSBzaW5nbGUgcGVyZm9ybWFuY2UgbWVhc3VyZQ0KDQpgYGB7cn0NCmF2Z19wcmVjaXNpb24gPC0gbWVhbihwcmVjaXNpb24pDQphdmdfcmVjYWxsIDwtIG1lYW4ocmVjYWxsKQ0KYXZnX3NwZWNpZmljaXR5IDwtIG1lYW4oc3BlY2lmaWNpdHkpDQpgYGANCg0KT3V0cHV0IHRoZSBldmFsdWF0aW9uIG1ldHJpY3MNCg0KYGBge3J9DQpwcmludChwYXN0ZSgiT3ZlcmFsbCBBY2N1cmFjeToiLCBhY2N1cmFjeSkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBQcmVjaXNpb246IiwgYXZnX3ByZWNpc2lvbikpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBSZWNhbGwgKFNlbnNpdGl2aXR5KToiLCBhdmdfcmVjYWxsKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFNwZWNpZmljaXR5OiIsIGF2Z19zcGVjaWZpY2l0eSkpDQpgYGANCg0KdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3M6DQoNCmBgYHtyfQ0KbWV0cmljcyA8LSBkYXRhLmZyYW1lKA0KICBDbGFzcyA9IHJvd25hbWVzKGNvbmZfbWF0cml4KSwNCiAgUHJlY2lzaW9uID0gcHJlY2lzaW9uLA0KICBSZWNhbGwgPSByZWNhbGwsDQogIFNwZWNpZmljaXR5ID0gc3BlY2lmaWNpdHkNCikNCmBgYA0KDQpQcmludCBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQobWV0cmljcykNCg0KYGBgDQoNCg0KUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpgYGB7cn0NCnJwYXJ0LnBsb3QodHJlZSkNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgR2luaSBJbmRleCg5MC8xMCk6KioNCg0KVGhlIGRlY2lzaW9uIHRyZWUgY2xhc3NpZmllcyBob2JiaWVzIGludG8gJ0FjYWRlbWljcycgKDEpLCAnQXJ0cycgKDIpLCBhbmQgJ1Nwb3J0cycgKDMpLiBBIGxhY2sgb2YgYSBzcG9ydHMgaG9iYnkgKCdDYXJlZXJfc3BydCcgPSAwKSBsZWFkcyB0byBhIDYzJSBjaGFuY2Ugb2YgZmFsbGluZyBpbnRvICdBY2FkZW1pY3MnLiBJZiBzb21lb25lIGlzIG5vdCBlbmdhZ2VkIGluIGFuIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMCkgYW5kICdXb25fYXJ0cycgaXMgMCBvciAyLCB0aGVyZSdzIGEgNDMlIHByb2JhYmlsaXR5IG9mIGFuICdBY2FkZW1pY3MnIGNhdGVnb3JpemF0aW9uLiBGb3IgaW5kaXZpZHVhbHMgZW5nYWdlZCBpbiBhbiBhcnRzIGhvYmJ5ICgnRmFudF9hcnRzJyA9IDEpIHdpdGggYSBoaWdoIGxldmVsIG9mIGFydHMgYWN0aXZpdHkgKCdUaW1lX2FydCcg4omlIDMpLCB0aGUgbGlrZWxpaG9vZCBvZiBhICdTcG9ydHMnIGNsYXNzaWZpY2F0aW9uIGlzIDI4JS4gVGhlc2UgbW9kZWwgc2hvdyBob3cgbGlrZWx5IHRoZSBtb2RlbCBpcyB0byBwcmVkaWN0IGVhY2ggaG9iYnkgYmFzZWQgb24gdGhlIGF0dHJpYnV0ZXMnIHNpZ25pZmljYW5jZSwgYXMgbGVhcm5lZCBmcm9tIHRoZSBkYXRhIHdpdGggYSA5MCUgdHJhaW5pbmcgcG9ydGlvbi4NCg0KDQoqKjMtR2luaSBpbmRleCg3MCUsMzAlKSoqDQoNCkluc3RhbGwgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FUb29scyIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQoNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQoNCmBgYA0KDQpTcGxpdCB0aGUgZGF0YXNldCwgNzAlIGZvciB0cmFpbmluZywgMzAlIGZvciB0ZXN0aW5nDQoNCmBgYHtyfQ0Kc3BsaXQgPC0gc2FtcGxlLnNwbGl0KEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIFNwbGl0UmF0aW8gPSAwLjcwKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdHJhaW5pbmcgc2V0ICg3MCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdHJhaW5pbmdfc2V0IDwtIHN1YnNldChIb2JieV9EYXRhLCBzcGxpdCA9PSBUUlVFKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdGVzdCBzZXQgKDIwJSBvZiB0aGUgZGF0YSkNCg0KYGBge3J9DQp0ZXN0X3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gRkFMU0UpDQoNCmBgYA0KDQpCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldA0KDQpgYGB7cn0NCnRyZWUgPC0gcnBhcnQoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gdHJhaW5pbmdfc2V0LCBtZXRob2QgPSAnY2xhc3MnKQ0KDQpgYGANCg0KTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQgdXNpbmcgdGhlIHRyZWUgbW9kZWwNCg0KYGBge3J9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHRyZWUsIHRlc3Rfc2V0LCB0eXBlID0gImNsYXNzIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4DQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IHRlc3Rfc2V0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBhY2N1cmFjeQ0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQoNCmBgYA0KDQpQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCmBgYHtyfQ0KcnBhcnQucGxvdCh0cmVlKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHaW5pIEluZGV4KDcwLzMwKToqKg0KDQpUaGUgZGVjaXNpb24gdHJlZSBzb3J0cyBob2JiaWVzIGludG8gJ0FjYWRlbWljcycgKDEpLCAnQXJ0cycgKDIpLCBhbmQgJ1Nwb3J0cycgKDMpLiBBIG5vbi1zcG9ydHMgaG9iYnkgKCdDYXJlZXJfc3BydCcgPSAwKSByZXN1bHRzIGluIGEgNjMlIHByb2JhYmlsaXR5IG9mIGFuICdBY2FkZW1pY3MnIGNhdGVnb3JpemF0aW9uLiBJZiAnRmFudF9hcnRzJyBpcyAwIGFuZCAnV29uX2FydHMnIGlzIDAgb3IgMiwgdGhlcmUncyBhIDQzJSBjaGFuY2Ugb2YgYmVpbmcgY2xhc3NpZmllZCBhcyAnQWNhZGVtaWNzJy4gQ29udmVyc2VseSwgZm9yIHRob3NlIGludm9sdmVkIGluIGFuIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMSkgd2l0aCBzaWduaWZpY2FudCBhcnRzIGFjdGl2aXR5ICgnVGltZV9hcnQnIOKJpSAzKSwgdGhlIG1vZGVsIGluZGljYXRlcyBhIDI4JSBwcm9iYWJpbGl0eSBvZiBhICdTcG9ydHMnIGhvYmJ5LiBUaGlzIGRlY2lzaW9uIHRyZWUgZGVtb25zdHJhdGVzIHRoZSBsaWtlbGlob29kIG9mIHByZWRpY3RpbmcgZWFjaCBob2JieSBiYXNlZCBvbiB0aGUgaW1wb3J0YW5jZSBvZiB0aGUgYXR0cmlidXRlcywgYXMgZGV0ZXJtaW5lZCBmcm9tIHRoZSBkYXRhIHRyYWluZWQgd2l0aCBhIDcwJSBwb3J0aW9uLg0KDQoqKkNvbXBhcmluZyBEZWNpc2lvbiBUcmVlIFJlc3VsdHMgVXNpbmcgR2luaSBJbmRleDoqKg0KDQpBY3Jvc3MgVGhyZWUgVHJhaW5pbmctVGVzdCBTaXplczogVGhlIHJlc3VsdHMgb2YgdGhlIGRlY2lzaW9uIHRyZWVzIGZyb20gdGhlIDkwLzEwLCA4MC8yMCwgYW5kIDcwLzMwIGRhdGFzZXQgc3BsaXRzLCB0aGVyZSBpcyBhIGNvbnNpc3RlbnQgcGF0dGVybjogJ0NhcmVlcl9zcHJ0JyBpcyBhbHdheXMgdGhlIHJvb3Qgbm9kZSwgYW5kIHRoZSBzdWJzZXF1ZW50IHNwbGl0cyBvbiAnV29uX2FydHMnIGFuZCAnRmFudF9hcnRzJyBhcmUgdGhlIHNhbWUgYWNyb3NzIGFsbCB0cmVlcy4gVGhpcyBjb25zaXN0ZW5jeSBpbiB0cmVlIHN0cnVjdHVyZSBhbmQgdGhlIHByb2JhYmlsaXRpZXMgZm9yIHByZWRpY3RpbmcgJ0FjYWRlbWljcycgYW5kICdTcG9ydHMnIGFjcm9zcyBkaWZmZXJlbnQgc3BsaXRzIHN1Z2dlc3QgYSBzdGFibGUgYW5kIHJvYnVzdCBtb2RlbCB0aGF0IGlzIHJlbGlhYmxlIHJlZ2FyZGxlc3Mgb2YgdGhlIHRyYWluaW5nIHNldCBzaXplLg0KDQp0aGUgYWNjdXJhY2llcyBvZiB0aHJlZSBkYXRhIHNwbGl0cyByZXZlYWxzIGRpc3RpbmN0IG91dGNvbWVzOiB0aGUgKDkwLDEwKSBzcGxpdCBsZWFkcyB3aXRoIHRoZSBoaWdoZXN0IGFjY3VyYWN5IGF0IDAuOTE4NzUsIHN1Z2dlc3RpbmcgdGhhdCBhIGxhcmdlciB0cmFpbmluZyBwb3J0aW9uIGlzIG1vcmUgZWZmZWN0aXZlIGluIHRoaXMgY2FzZS4gVGhlICg3MCwzMCkgc3BsaXQgZm9sbG93cyB3aXRoIGFuIGFjY3VyYWN5IG9mIDAuOTEwNjAsIHNob3dpbmcgc3Ryb25nIHBlcmZvcm1hbmNlIGV2ZW4gd2l0aCBhIGxhcmdlciB0ZXN0IHNldC4gSG93ZXZlciwgdGhlIGNvbW1vbmx5IHVzZWQgKDgwLDIwKSBzcGxpdCBsYWdzIHNsaWdodGx5IGJlaGluZCwgYWNoaWV2aW5nIGFuIGFjY3VyYWN5IG9mIDAuOTA2MjUuIFRoaXMgY29tcGFyaXNvbiBoaWdobGlnaHRzIHRoZSBpbXBhY3Qgb2YgdmFyeWluZyB0cmFpbmluZyBhbmQgdGVzdGluZyBwcm9wb3J0aW9ucyBvbiBtb2RlbCBhY2N1cmFjeS4NCg0KDQoNCioqR2FpbiBSYXRpbyoqDQpUaGUgdGhpcmQgY3JpdGVyaW9uIGVtcGxveWVkIGZvciBidWlsZGluZyB0aGUgZGVjaXNpb24gdHJlZSBpcyBHYWluIFJhdGlvLiBHYWluIFJhdGlvIHN0YW5kcyBvdXQgYXMgYSBzaWduaWZpY2FudCBtZXRyaWMgaW4gZGVjaXNpb24gdHJlZSBhbGdvcml0aG1zLCBlc3BlY2lhbGx5IGluIHNjZW5hcmlvcyBpbnZvbHZpbmcgY2F0ZWdvcmljYWwgdGFyZ2V0IHZhcmlhYmxlcy4gSXQgbm9ybWFsaXplcyB0aGUgcmVkdWN0aW9uIGluIGVudHJvcHkgYnkgdGFraW5nIGludG8gYWNjb3VudCB0aGUgcG90ZW50aWFsIGluZm9ybWF0aW9uIGNvbnRlbnQgb2YgdGhlIGZlYXR1cmUuIFRoaXMgbm9ybWFsaXphdGlvbiBwcm9jZXNzIG1ha2VzIEdhaW4gUmF0aW8gcGFydGljdWxhcmx5IHN1aXRhYmxlIGZvciBkYXRhc2V0cyB3aXRoIGNhdGVnb3JpY2FsIHRhcmdldCB2YXJpYWJsZXMuIEJ5IGZhY3RvcmluZyBpbiB0aGUgaW50cmluc2ljIGluZm9ybWF0aW9uIG9mIGEgc3BsaXQsIEdhaW4gUmF0aW8gZWZmZWN0aXZlbHkgbWl0aWdhdGVzIGJpYXMgdG93YXJkcyBmZWF0dXJlcyB3aXRoIGhpZ2hlciBsZXZlbHMsIGVuc3VyaW5nIGEgbW9yZSBiYWxhbmNlZCBldmFsdWF0aW9uIG9mIGRpZmZlcmVudCBhdHRyaWJ1dGVzLg0KDQoqKjEtR2FpbiByYXRpbyg5MCUsMTAlKSoqDQoNCkluc3RhbGwgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoIkM1MCIpDQppbnN0YWxsLnBhY2thZ2VzKCJwcmludHIiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KYGBgDQoNCkxvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCg0KbGlicmFyeShDNTApDQpsaWJyYXJ5KHByaW50cikNCmxpYnJhcnkoY2FyZXQpDQpgYGANCg0KU2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTk1OCkNCg0KYGBgDQoNClNwbGl0dGluZyB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IHNldHMNCg0KYGBge3J9DQp0cmFpbl9pbmRpY2VzIDwtIHNhbXBsZSgxOm5yb3coSG9iYnlfRGF0YSksIDAuOSAqIG5yb3coSG9iYnlfRGF0YSkpDQpIb2JieS50cmFpbiA8LSBIb2JieV9EYXRhW3RyYWluX2luZGljZXMsIF0NCkhvYmJ5LnRlc3QgPC0gSG9iYnlfRGF0YVstdHJhaW5faW5kaWNlcywgXQ0KDQpgYGANCg0KVHJhaW5pbmcgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwNCg0KYGBge3J9DQptb2RlbCA8LSBDNS4wKGBQcmVkaWN0ZWQgSG9iYnlgIH4gLiwgZGF0YSA9IEhvYmJ5LnRyYWluLCBjb250cm9sID0gQzUuMENvbnRyb2woQ0YgPSAwLjAxKSkNCg0KYGBgDQoNCk1ha2luZyBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQNCg0KYGBge3J9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gSG9iYnkudGVzdCwgdHlwZSA9ICdjbGFzcycpDQpgYGANCg0KQ3JlYXRlIGEgY29uZnVzaW9uIG1hdHJpeCBmcm9tIHRoZSBwcmVkaWN0aW9ucyBhbmQgYWN0dWFsIHZhbHVlcw0KDQpgYGB7cn0NCmNvbmZfbWF0cml4IDwtIHRhYmxlKFByZWRpY3RlZCA9IHByZWRpY3Rpb25zLCBBY3R1YWwgPSBIb2JieS50ZXN0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBhbmQgcHJpbnQgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbA0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpwcmludChwYXN0ZSgnQWNjdXJhY3kgb24gdGVzdCBkYXRhIGlzOicsIGFjY3VyYWN5KSkNCmBgYA0KDQpJbml0aWFsaXplIHZlY3RvcnMgdG8gaG9sZCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCnByZWNpc2lvbiA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KcmVjYWxsIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpzcGVjaWZpY2l0eSA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KYGBgDQoNCkNhbGN1bGF0ZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6bnJvdyhjb25mX21hdHJpeCkpIHsNCiAgVFAgPC0gY29uZl9tYXRyaXhbaSwgaV0NCiAgRlAgPC0gc3VtKGNvbmZfbWF0cml4WywgaV0pIC0gVFANCiAgRk4gPC0gc3VtKGNvbmZfbWF0cml4W2ksIF0pIC0gVFANCiAgVE4gPC0gc3VtKGNvbmZfbWF0cml4KSAtIFRQIC0gRlAgLSBGTg0KICANCiAgcHJlY2lzaW9uW2ldIDwtIFRQIC8gKFRQICsgRlApDQogIHJlY2FsbFtpXSA8LSBUUCAvIChUUCArIEZOKQ0KICBzcGVjaWZpY2l0eVtpXSA8LSBUTiAvIChUTiArIEZQKQ0KfQ0KYGBgDQoNCkF2ZXJhZ2UgdGhlIG1ldHJpY3MgaWYgeW91IHdhbnQgYSBzaW5nbGUgcGVyZm9ybWFuY2UgbWVhc3VyZQ0KDQpgYGB7cn0NCmF2Z19wcmVjaXNpb24gPC0gbWVhbihwcmVjaXNpb24pDQphdmdfcmVjYWxsIDwtIG1lYW4ocmVjYWxsKQ0KYXZnX3NwZWNpZmljaXR5IDwtIG1lYW4oc3BlY2lmaWNpdHkpDQpgYGANCg0KT3V0cHV0IHRoZSBldmFsdWF0aW9uIG1ldHJpY3MNCg0KYGBge3J9DQpwcmludChwYXN0ZSgiT3ZlcmFsbCBBY2N1cmFjeToiLCBhY2N1cmFjeSkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBQcmVjaXNpb246IiwgYXZnX3ByZWNpc2lvbikpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBSZWNhbGwgKFNlbnNpdGl2aXR5KToiLCBhdmdfcmVjYWxsKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFNwZWNpZmljaXR5OiIsIGF2Z19zcGVjaWZpY2l0eSkpDQpgYGANCg0KcHJpbnQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3M6DQoNCmBgYHtyfQ0KDQptZXRyaWNzIDwtIGRhdGEuZnJhbWUoDQogIENsYXNzID0gcm93bmFtZXMoY29uZl9tYXRyaXgpLA0KICBQcmVjaXNpb24gPSBwcmVjaXNpb24sDQogIFJlY2FsbCA9IHJlY2FsbCwNCiAgU3BlY2lmaWNpdHkgPSBzcGVjaWZpY2l0eQ0KKQ0KYGBgDQoNClByaW50IG1ldHJpY3MNCg0KYGBge3J9DQpwcmludChtZXRyaWNzKQ0KYGBgDQoNCkdlbmVyYXRlIGFuZCBwcmludCBhZGRpdGlvbmFsIHBlcmZvcm1hbmNlIG1ldHJpY3MgdXNpbmcgY2FyZXQgcGFja2FnZQ0KDQpgYGB7cn0NCmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9ucywgSG9iYnkudGVzdCRgUHJlZGljdGVkIEhvYmJ5YCkNCmBgYA0KDQpQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCmBgYHtyfQ0KcGxvdChtb2RlbCkNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgR2FpbiBSYXRpbyg5MCUvMTAlKToqKg0KDQpJbiBGaXJzdCBUcmVlICx3ZSBkZXZpZGUgZGF0YXNldCBpbnRvIHRyYWluaW5nIHNldCBhbmQgdGVzdCBzZXQgd2l0aCBzaXplKCU5MCwlMTApIHJlc3BlY3RpdmVseS4gQXMgeW91IGNhbiBzZWUgaW4gdGhlIGZpZ3VyZSwgdGhlIHJvb3Qgbm9kZSBpcyAiQ2FyZWVyX3NwcnQiICwgY2xhc3MgMSgiQWNhZGVtaWNzIiksIGNsYXNzIDIoIkFydHMiKSwgYW5kIGNsYXNzIDMgKCJTcG9ydHMiKS4NCg0KTm9kZSAiQ2FyZWVyX3NwcnQiIFRoZSBmaXJzdCBkZWNpc2lvbiBpcyBiYXNlZCBvbiB3aGV0aGVyIHRoZSB2YWx1ZSBvZiB0aGUgIkNhcmVlcl9zcHJ0IiBhdHRyaWJ1dGUgaXMgMC4gdGhlbiBjaGVjayBpZiAiV29uX2FydHMiIGlzIGVpdGhlciAwIG9yIDIuSWYiIFdvbl9hcnRzIiBpcyAwIG9yIDIgLCBwcmVkaWN0IkFjYWRlbWljIi50aGVuIGNoZWNrIElmICJGYW50X2FydHMiIGlzIDEgImFuZCBXb25fYXJ0cyIgaXMgMSwgcHJlZGljdCAiQXJ0cyIuSWYgIkZhbnRfYXJ0cyIgaXMgMCBhbmQgV29uX2FydHMgaXMgMCBvciAyLCB0aGVuIGNoZWNrIGlmIE9seW1waWFkX1BhcnRpY2lwYXRpb24gaXMgMS5JZiBPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIGlzIDEgcHJlZGljdCAiQWNhZGVtaWNzIi4oV2hlbiBPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIGlzIDApIElmICJPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIiBpcyAwIGFuZCAiRmFudF9hcnRzIiBpcyAwLCB0aGVuIGNoZWNrIGlmICJHcmFzcF9wb3ciIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byA0ICwgcHJlZGljdCBjbGFzcyJBcnRzIi5XaGVuIENhcmVlcl9zcHJ0IGlzIDEsIHRoZW4gY2hlY2sgaWYgIkZhbnRfYXJ0cyIgaXMgMCB0aGVuIHByZWRpY3QgIlNwb3J0cyIuIElmICJGYW50X2FydHMiIGlzIDEsIHRoZW4gY2hlY2sgaWYgIlRpbWVfYXJ0IiBpcyBncmVhdGVyIHRoYW4gMi5jaGVjayBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byAyLCB0aGVuIGNoZWNrIGlmICJBY3Rfc3BydCIgaXMgMSBvciAwLklmIEFjdF9zcHJ0IGlzIDEsIHByZWRpY3QgIlNwb3J0cyIuIElmIEFjdF9zcHJ0IGlzIDAsIHRoZW4gY2hlY2sgaWYgT2x5bXBpYWRfUGFydGljaXBhdGlvbiBpcyAwIHByZWRpY3QgIkFydHMiLiBJZiAiT2x5bXBpYWRfUGFydGljaXBhdGlvbiIgaXMgMSwgcHJlZGljdCAiQWNhZGVtaWNzIi5XaGVuIFRpbWVfYXJ0IGlzIGdyZWF0ZXIgdGhhbiAyICxJZiBXb25fYXJ0cyBpcyAwLCBwcmVkaWN0IGNsYXNzICJTcG9ydHMiLg0KDQoNCioqMi1HYWluIHJhdGlvKDgwJSwyMCUpKioNCg0KSW5zdGFsbCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiQzUwIikNCmluc3RhbGwucGFja2FnZXMoInByaW50ciIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpgYGANCg0KTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KEM1MCkNCmxpYnJhcnkocHJpbnRyKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxOTU4KQ0KDQpgYGANCg0KU3BsaXR0aW5nIHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cw0KDQpgYGB7cn0NCnRyYWluX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhIb2JieV9EYXRhKSwgMC44ICogbnJvdyhIb2JieV9EYXRhKSkNCkhvYmJ5LnRyYWluIDwtIEhvYmJ5X0RhdGFbdHJhaW5faW5kaWNlcywgXQ0KSG9iYnkudGVzdCA8LSBIb2JieV9EYXRhWy10cmFpbl9pbmRpY2VzLCBdDQoNCmBgYA0KDQpUcmFpbmluZyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbA0KDQpgYGB7cn0NCm1vZGVsIDwtIEM1LjAoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gSG9iYnkudHJhaW4sIGNvbnRyb2wgPSBDNS4wQ29udHJvbChDRiA9IDAuMDEpKQ0KDQpgYGANCg0KTWFraW5nIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBIb2JieS50ZXN0LCB0eXBlID0gJ2NsYXNzJykNCmBgYA0KDQpDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4IGZyb20gdGhlIHByZWRpY3Rpb25zIGFuZCBhY3R1YWwgdmFsdWVzDQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IEhvYmJ5LnRlc3QkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KQ2FsY3VsYXRlIGFuZCBwcmludCB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCnByaW50KHBhc3RlKCdBY2N1cmFjeSBvbiB0ZXN0IGRhdGEgaXM6JywgYWNjdXJhY3kpKQ0KYGBgDQoNCkluaXRpYWxpemUgdmVjdG9ycyB0byBob2xkIHRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KcHJlY2lzaW9uIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpyZWNhbGwgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnNwZWNpZmljaXR5IDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpgYGANCg0KQ2FsY3VsYXRlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpmb3IgKGkgaW4gMTpucm93KGNvbmZfbWF0cml4KSkgew0KICBUUCA8LSBjb25mX21hdHJpeFtpLCBpXQ0KICBGUCA8LSBzdW0oY29uZl9tYXRyaXhbLCBpXSkgLSBUUA0KICBGTiA8LSBzdW0oY29uZl9tYXRyaXhbaSwgXSkgLSBUUA0KICBUTiA8LSBzdW0oY29uZl9tYXRyaXgpIC0gVFAgLSBGUCAtIEZODQogIA0KICBwcmVjaXNpb25baV0gPC0gVFAgLyAoVFAgKyBGUCkNCiAgcmVjYWxsW2ldIDwtIFRQIC8gKFRQICsgRk4pDQogIHNwZWNpZmljaXR5W2ldIDwtIFROIC8gKFROICsgRlApDQp9DQpgYGANCg0KQXZlcmFnZSB0aGUgbWV0cmljcyBpZiB5b3Ugd2FudCBhIHNpbmdsZSBwZXJmb3JtYW5jZSBtZWFzdXJlDQoNCmBgYHtyfQ0KYXZnX3ByZWNpc2lvbiA8LSBtZWFuKHByZWNpc2lvbikNCmF2Z19yZWNhbGwgPC0gbWVhbihyZWNhbGwpDQphdmdfc3BlY2lmaWNpdHkgPC0gbWVhbihzcGVjaWZpY2l0eSkNCmBgYA0KDQpPdXRwdXQgdGhlIGV2YWx1YXRpb24gbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KHBhc3RlKCJPdmVyYWxsIEFjY3VyYWN5OiIsIGFjY3VyYWN5KSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFByZWNpc2lvbjoiLCBhdmdfcHJlY2lzaW9uKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFJlY2FsbCAoU2Vuc2l0aXZpdHkpOiIsIGF2Z19yZWNhbGwpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgU3BlY2lmaWNpdHk6IiwgYXZnX3NwZWNpZmljaXR5KSkNCmBgYA0KDQpwcmludCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzczoNCg0KYGBge3J9DQoNCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQpgYGANCg0KR2VuZXJhdGUgYW5kIHByaW50IGFkZGl0aW9uYWwgcGVyZm9ybWFuY2UgbWV0cmljcyB1c2luZyBjYXJldCBwYWNrYWdlDQoNCmBgYHtyfQ0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zLCBIb2JieS50ZXN0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNClBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCg0KYGBge3J9DQpwbG90KG1vZGVsKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHYWluIFJhdGlvKDgwLzIwKToqKg0KDQpUaGUgZGVjaXNpb24gdHJlZSBkZXBpY3RlZCBjbGFzc2lmaWVzIGhvYmJpZXMgaW50byAnQWNhZGVtaWNzJyAoMSksICdBcnRzJyAoMiksIGFuZCAnU3BvcnRzJyAoMykuIEl0IHN0YXJ0cyB3aXRoICdDYXJlZXJfc3BydCcgYSB2YWx1ZSBvZiAwIGxlYWRzIHRvICdXb25fYXJ0cycuIElmICdXb25fYXJ0cycgaXMgMCBvciAyLCB0aGUgbW9kZWwgc3VnZ2VzdHMgJ0FjYWRlbWljcycgb3IgJ0FydHMnLiBJZiAnQ2FyZWVyX3NwcnQnIGlzIDEsICdGYW50X2FydHMnIGlzIGNvbnNpZGVyZWQgbmV4dDsgYSB2YWx1ZSBvZiAwIGFmdGVyICdXb25fYXJ0cycgYmVpbmcgMSBwb2ludHMgdG93YXJkcyAnQXJ0cycsIHdoaWxlIGEgdmFsdWUgb2YgMSBsZWFkcyB0byAnT2x5bXBpYWRfUGFydGljaXBhdGlvbicsIHdoaWNoLCBpZiAxLCBpbmRpY2F0ZXMgJ0FjYWRlbWljcycuIENvbnZlcnNlbHksIGEgaGlnaCAnR3Jhc3BfcG93JyAoXD40KSBwcmVkaWN0cyAnU3BvcnRzJy4NCg0KDQoNCioqMy1HYWluIHJhdGlvKDcwJSwzMCUpKioNCg0KSW5zdGFsbCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiQzUwIikNCmluc3RhbGwucGFja2FnZXMoInByaW50ciIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpgYGANCg0KTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KEM1MCkNCmxpYnJhcnkocHJpbnRyKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxOTU4KQ0KDQpgYGANCg0KU3BsaXR0aW5nIHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cw0KDQpgYGB7cn0NCnRyYWluX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhIb2JieV9EYXRhKSwgMC43ICogbnJvdyhIb2JieV9EYXRhKSkNCkhvYmJ5LnRyYWluIDwtIEhvYmJ5X0RhdGFbdHJhaW5faW5kaWNlcywgXQ0KSG9iYnkudGVzdCA8LSBIb2JieV9EYXRhWy10cmFpbl9pbmRpY2VzLCBdDQoNCmBgYA0KDQpUcmFpbmluZyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbA0KDQpgYGB7cn0NCm1vZGVsIDwtIEM1LjAoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gSG9iYnkudHJhaW4sIGNvbnRyb2wgPSBDNS4wQ29udHJvbChDRiA9IDAuMDEpKQ0KDQpgYGANCg0KTWFraW5nIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBIb2JieS50ZXN0LCB0eXBlID0gJ2NsYXNzJykNCmBgYA0KDQpDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4IGZyb20gdGhlIHByZWRpY3Rpb25zIGFuZCBhY3R1YWwgdmFsdWVzDQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IEhvYmJ5LnRlc3QkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KQ2FsY3VsYXRlIGFuZCBwcmludCB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCnByaW50KHBhc3RlKCdBY2N1cmFjeSBvbiB0ZXN0IGRhdGEgaXM6JywgYWNjdXJhY3kpKQ0KYGBgDQoNCkluaXRpYWxpemUgdmVjdG9ycyB0byBob2xkIHRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KcHJlY2lzaW9uIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpyZWNhbGwgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnNwZWNpZmljaXR5IDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpgYGANCg0KQ2FsY3VsYXRlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpmb3IgKGkgaW4gMTpucm93KGNvbmZfbWF0cml4KSkgew0KICBUUCA8LSBjb25mX21hdHJpeFtpLCBpXQ0KICBGUCA8LSBzdW0oY29uZl9tYXRyaXhbLCBpXSkgLSBUUA0KICBGTiA8LSBzdW0oY29uZl9tYXRyaXhbaSwgXSkgLSBUUA0KICBUTiA8LSBzdW0oY29uZl9tYXRyaXgpIC0gVFAgLSBGUCAtIEZODQogIA0KICBwcmVjaXNpb25baV0gPC0gVFAgLyAoVFAgKyBGUCkNCiAgcmVjYWxsW2ldIDwtIFRQIC8gKFRQICsgRk4pDQogIHNwZWNpZmljaXR5W2ldIDwtIFROIC8gKFROICsgRlApDQp9DQpgYGANCg0KQXZlcmFnZSB0aGUgbWV0cmljcyBpZiB5b3Ugd2FudCBhIHNpbmdsZSBwZXJmb3JtYW5jZSBtZWFzdXJlDQoNCmBgYHtyfQ0KYXZnX3ByZWNpc2lvbiA8LSBtZWFuKHByZWNpc2lvbikNCmF2Z19yZWNhbGwgPC0gbWVhbihyZWNhbGwpDQphdmdfc3BlY2lmaWNpdHkgPC0gbWVhbihzcGVjaWZpY2l0eSkNCmBgYA0KDQpPdXRwdXQgdGhlIGV2YWx1YXRpb24gbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KHBhc3RlKCJPdmVyYWxsIEFjY3VyYWN5OiIsIGFjY3VyYWN5KSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFByZWNpc2lvbjoiLCBhdmdfcHJlY2lzaW9uKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFJlY2FsbCAoU2Vuc2l0aXZpdHkpOiIsIGF2Z19yZWNhbGwpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgU3BlY2lmaWNpdHk6IiwgYXZnX3NwZWNpZmljaXR5KSkNCmBgYA0KDQpwcmludCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzczoNCg0KYGBge3J9DQoNCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQpgYGANCg0KR2VuZXJhdGUgYW5kIHByaW50IGFkZGl0aW9uYWwgcGVyZm9ybWFuY2UgbWV0cmljcyB1c2luZyBjYXJldCBwYWNrYWdlDQoNCmBgYHtyfQ0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zLCBIb2JieS50ZXN0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNClBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCg0KYGBge3J9DQpwbG90KG1vZGVsKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHYWluIFJhdGlvKDcwJS8zMCUpOioqDQoNCkluIFRoaXJkIFRyZWUgLHdlIGRldmlkZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgc2V0IGFuZCB0ZXN0IHNldCB3aXRoIHNpemUoJTcwLCUzMCkgcmVzcGVjdGl2ZWx5LiBBcyB5b3UgY2FuIHNlZSBpbiB0aGUgZmlndXJlLCB0aGUgcm9vdCBub2RlIGlzICJDYXJlZXJfc3BydCIgLCBjbGFzcyAxKCJBY2FkZW1pY3MiKSwgY2xhc3MgMigiQXJ0cyIpLCBhbmQgY2xhc3MgMyAoIlNwb3J0cyIpLg0KDQpUaGUgZmlyc3QgZGVjaXNpb24gaXMgYmFzZWQgb24gd2hldGhlciB0aGUgdmFsdWUgb2YgdGhlICJDYXJlZXJfc3BydCIgYXR0cmlidXRlIGlzIDAuSWYgIkNhcmVlcl9zcHJ0IiBpcyAwLCB0aGVuIGNoZWNrIGlmICJXb25fYXJ0cyIgaXMgZWl0aGVyIDAgb3IgMi5wcmVkaWN0ICJBY2FkZW1pY3MiLklmICJXb25fYXJ0cyIgaXMgMSwgdGhlbiBjaGVjayBpZiAiRmFudF9hcnRzIiBpcyAxLHByZWRpY3QgIkFydHMiLklmICIiRmFudF9hcnRzIGlzIDAgYW5kIldvbl9hcnRzIiBpcyAwIG9yIDIsIHRoZW4gY2hlY2sgaWYgIlRpbWVfYXJ0IiBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gMixwcmVkaWN0ICJBY2FkZW1pY3MiLiBJZiAiVGltZV9hcnQiIGlzIGdyZWF0ZXIgdGhhbiAyLCBwcmVkaWN0ICJBcnRzIi4NCg0KSWYgIkNhcmVlcl9zcHJ0ImlzIDEsIHRoZW4gY2hlY2sgaWYgIkZhbnRfYXJ0cyIgaXMgMCwgcHJlZGljdCAiU3BvcnRzIi5JZiAiRmFudF9hcnRzImlzIDEsIHRoZW4gY2hlY2sgaWYgIlRpbWVfYXJ0IiBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gMiBjaGVjayBpZiAiQWN0X3NwcnQiIGlzIDAscHJlZGljdCAiQWNhZGVtaWNzIiAuSWYgIkFjdF9zcHJ0IiBpcyAxIHByZWRpY3QgIlNwb3J0cyIuIGlmICJUaW1lX2FydCIgaXMgZ3JlYXRlciB0aGFuIDIsIHRoZW4gY2hlY2sgaWYgIldvbl9hcnRzIiBpcyAwIHByZWRpY3QgIlNwb3J0cyIuSWYgIldvbl9hcnRzIiBpcyAxIG9yIDIgcHJlZGljdCAiQXJ0cyIuDQoNCioqQ29tcGFyaW5nIERlY2lzaW9uIFRyZWUgUmVzdWx0cyBVc2luZyBHYWluIFJhdGlvKioNCg0KQWNyb3NzIFRocmVlIFRyYWluaW5nLVRlc3QgU2l6ZXM6IFRoZSBhY2N1cmFjeSByYXRlcyAtMC44OTQ0IGZvciB0aGUgOTA6MTAgc3BsaXQsIDAuODkzOSBmb3IgdGhlIDcwOjMwIHNwbGl0LCBhbmQgMC44ODc5IGZvciB0aGUgODA6MjAgc3BsaXQgLS0gaW5kaWNhdGUgb25seSBzbGlnaHQgdmFyaWF0aW9ucywgd2l0aCB0aGUgOTA6MTAgc3BsaXQgYmVpbmcgbWFyZ2luYWxseSBiZXR0ZXIuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tQ2x1c3RlcmluZy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qKg0KDQoNCg0KYGBge3J9DQpzdHIoSG9iYnlfcHJvYykNCmBgYA0KDQoqKlRvIGNvbnZlcnQgdHlwZSBmcm9tIGZhY3RvciBjb2x1bW5zIHRvIG51bWVyaWMqKg0KDQpgYGB7cn0NCg0KSG9iYnlfcHJvYyRGYXZfc3ViIDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJEZhdl9zdWIpDQoNCkhvYmJ5X3Byb2MkT2x5bXBpYWRfUGFydGljaXBhdGlvbiA8LWFzLm51bWVyaWMoSG9iYnlfcHJvYyRPbHltcGlhZF9QYXJ0aWNpcGF0aW9uKQ0KDQpIb2JieV9wcm9jJFByb2plY3RzIDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJFByb2plY3RzKQ0KDQpIb2JieV9wcm9jJFNjaG9sYXJzaGlwIDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJFNjaG9sYXJzaGlwKQ0KDQpIb2JieV9wcm9jJENhcmVlcl9zcHJ0IDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJENhcmVlcl9zcHJ0KQ0KDQpIb2JieV9wcm9jJEFjdF9zcHJ0IDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJEFjdF9zcHJ0KQ0KDQpIb2JieV9wcm9jJEZhbnRfYXJ0cyA8LWFzLm51bWVyaWMoSG9iYnlfcHJvYyRGYW50X2FydHMpDQoNCkhvYmJ5X3Byb2MkV29uX2FydHMgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkV29uX2FydHMpDQoNCkhvYmJ5X3Byb2MkYFByZWRpY3RlZCBIb2JieWAgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkYFByZWRpY3RlZCBIb2JieWAgKQ0KYGBgDQoNCg0KYGBge3J9DQpzdHIoSG9iYnlfcHJvYykNCmBgYA0KDQoqKlNjYWxlZCBBbGwgQ29sdW1ucyoqDQoNCmBgYHtyfQ0KSG9iYnlfcHJvYyA8LXNjYWxlKEhvYmJ5X3Byb2MpDQpgYGANCg0KKipEYXRhIHNldCB3aXRob3V0IGdyb3VuZCB0cnV0aCoqDQoNCmBgYHtyfQ0KSG9iYnlfRGF0YTIgPC0gSG9iYnlfcHJvY1ssICEoY29sbmFtZXMoSG9iYnlfcHJvYykgJWluJSBjKCJQcmVkaWN0IEhvYmJ5IikpXQ0KYGBgDQoNCg0KT3VyIGRhdGEgc2V0IGV4aGliaXRzIGEgYmFsYW5jZWQgZGlzdHJpYnV0aW9uIGFtb25nIGNsYXNzIGxhYmVscywgd2l0aA0KImFjYWRlbWljLCIgImFydCwiIGFuZCAic3BvcnQiIGNvbnN0aXR1dGluZyA0My42JSwgMjUuNiUsIGFuZCAzMC44JSBvZg0KdGhlIHRvdGFsLCByZXNwZWN0aXZlbHkuIFRoaXMgYmFsYW5jZWQgZGlzdHJpYnV0aW9uIGlzIGFkdmFudGFnZW91cyBmb3INCmJvdGggY2xhc3NpZmljYXRpb24gYW5kIGNsdXN0ZXJpbmcgdGFza3MuIEluIGNsYXNzaWZpY2F0aW9uLCBhIGJhbGFuY2VkDQpkYXRhIHNldCBoZWxwcyBwcmV2ZW50IHRoZSBtb2RlbCBmcm9tIGZhdm9yaW5nIG9uZSBjbGFzcyBvdmVyIHRoZQ0Kb3RoZXJzLCBlbnN1cmluZyB0aGF0IHRoZSBsZWFybmluZyBhbGdvcml0aG0gaXMgZXhwb3NlZCB0byBhDQpyZXByZXNlbnRhdGl2ZSBzZXQgb2YgZXhhbXBsZXMgZnJvbSBlYWNoIGNhdGVnb3J5LiBUaGlzIGJhbGFuY2UgcHJvbW90ZXMNCnRoZSBkZXZlbG9wbWVudCBvZiBhIG1vZGVsIHRoYXQgZ2VuZXJhbGl6ZXMgd2VsbCBhY3Jvc3MgYWxsIGNsYXNzZXMsDQplbmhhbmNpbmcgaXRzIHByZWRpY3RpdmUgcGVyZm9ybWFuY2Ugb24gbmV3LCB1bnNlZW4gZGF0YS4gSW4gY2x1c3RlcmluZywNCmEgYmFsYW5jZWQgZGF0YSBzZXQgYWlkcyBpbiBmb3JtaW5nIGNsdXN0ZXJzIHRoYXQgYXJlIG1vcmUgZXZlbmx5DQpkaXN0cmlidXRlZCwgYWxsb3dpbmcgZm9yIGEgY29tcHJlaGVuc2l2ZSB1bmRlcnN0YW5kaW5nIG9mIHBhdHRlcm5zIGFuZA0KcmVsYXRpb25zaGlwcyBhY3Jvc3MgZGl2ZXJzZSBjYXRlZ29yaWVzLiBCYWxhbmNlZCBkYXRhIHNldHMgb2Z0ZW4gbGVhZA0KdG8gbW9yZSBhY2N1cmF0ZSBhbmQgZmFpciBjbHVzdGVyaW5nIHJlc3VsdHMsIGVuYWJsaW5nIG1lYW5pbmdmdWwNCmluc2lnaHRzIGludG8gdGhlIHVuZGVybHlpbmcgc3RydWN0dXJlcyB3aXRoaW4gZWFjaCBjbGFzcy4NCg0KKipyZXF1aXJlIHBhY2thZ2VzL0xpYnJhcnkqKg0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSANCmluc3RhbGwucGFja2FnZXMoIm1hZ3JpdHRyIikNCmluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCmxpYnJhcnkoZmFjdG9leHRyYSkgDQpsaWJyYXJ5KGNsdXN0ZXIpDQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCg0KDQoqKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLWstbWVhbnNjbHVzdGVyaW5nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KKipWYWxpZGF0aW9uOioqDQoNCkRldGVybWluaW5nIHRoZSByaWdodCBudW1iZXIgb2YgY2x1c3RlcnMgYmVmb3JlIHN0YXJ0aW5nIHRoZSBjbHVzdGVyaW5nDQpwcm9jZXNzIGlzIGxpa2UgbWFraW5nIHN1cmUgeW91IGhhdmUgdGhlIGNvcnJlY3Qtc2l6ZWQgcHV6emxlIHBpZWNlcw0KYmVmb3JlIHB1dHRpbmcgdGhlIHB1enpsZSB0b2dldGhlci4gSXQncyBpbXBvcnRhbnQgYmVjYXVzZSBzb21lDQpjbHVzdGVyaW5nIGFsZ29yaXRobXMsIGxpa2UgSy1tZWFucywgcmVxdWlyZSBzdWNoIGEgcGFyYW1ldGVyLiBJbg0KYWRkaXRpb24gdG8gdGhhdCwgaXQgaGVscHMgbWFrZSB0aGUgY2x1c3RlcmluZyBtb3JlIGFjY3VyYXRlIGFuZCB1c2VmdWwuDQpJZiB5b3Uga25vdyB0aGUgcmlnaHQgbnVtYmVyIGJlZm9yZWhhbmQsIGl0IHNhdmVzIHRpbWUgYW5kIGhlbHBzIG1ha2UNCnRoZSB3aG9sZSBwcm9jZXNzIG1vcmUgZWZmaWNpZW50IGFuZCB0aGUgcmVzdWx0cyBtb3JlIHJlbGlhYmxlLg0KDQoqKmNvbXB1dGUgYXZlcmFnZSBzaWxob3VldHRlIGZvciBrIGNsdXN0ZXJzIHVzaW5nIHNpbGhvdWV0dGUoKSBGb3Igay1tZWFuKioNCg0KYGBge3J9DQpzaWxob3VldHRlX3Njb3JlIDwtIGZ1bmN0aW9uKGspIHsNCiAga20gPC0ga21lYW5zKEhvYmJ5X0RhdGEyLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMjUpDQogIHNzIDwtIHNpbGhvdWV0dGUoa20kY2x1c3RlciwgZGlzdChIb2JieV9EYXRhMikpDQogIHNpbCA8LSBtZWFuKHNzWywgM10pDQogIHJldHVybihzaWwpDQp9DQoNCiMgayBjbHVzdGVyIHJhbmdlIGZyb20gMiB0byAxMA0KayA8LSAyOjEwDQoNCiMgY2FsbCBmdW5jdGlvbiBmb3IgZWFjaCBrIHZhbHVlDQphdmdfc2lsIDwtIHNhcHBseShrLCBzaWxob3VldHRlX3Njb3JlKQ0KDQojIHBsb3QgdGhlIHJlc3VsdHMNCnBsb3QoaywgYXZnX3NpbCwgdHlwZSA9ICdiJywgeGxhYiA9ICdOdW1iZXIgb2YgY2x1c3RlcnMnLCB5bGFiID0gJ0F2ZXJhZ2UgU2lsaG91ZXR0ZSBTY29yZXMnLCBmcmFtZSA9IEZBTFNFKQ0KYGBgDQoNClRoZSByZXN1bHRzIGluZGljYXRlIHRoYXQgdGhlIGhpZ2hlc3QgYXZlcmFnZSBzaWxob3VldHRlIHNjb3JlcyBpbmRpY2F0ZQ0KdGhlIHF1YWxpdHkgb2YgdGhlIGNsdXN0ZXJzIGFuZCBzdWdnZXN0IGJldHRlci1kZWZpbmVkIGFuZCBtb3JlDQpzZXBhcmF0ZWQgY2x1c3RlcnMsIHdpdGggZWFjaCBwb2ludCBoYXZpbmcgYSBoaWdoIGRlZ3JlZSBvZiBzaW1pbGFyaXR5DQp0byBpdHMgb3duIGNsdXN0ZXIgYW5kIGEgbG93ZXIgc2ltaWxhcml0eSB0byBuZWlnaGJvcmluZyBjbHVzdGVycy4gVGhlDQpoaWdoZXN0IGF2ZXJhZ2Ugc2lsaG91ZXR0ZSBzY29yZXMgd2VyZSBvYnNlcnZlZCBmb3IgayB2YWx1ZXMgb2YgMywgMiwNCmFuZCA0LCBzbyB0aGVzZSBhcmUgdGhlIG9wdGltYWwgbnVtYmVycy4gVGhlc2UgdmFsdWVzIHdpbGwgYmUgZW1wbG95ZWQNCmluIHN1YnNlcXVlbnQgay1tZWFucyBjbHVzdGVyaW5nIGFuYWx5c2VzLg0KDQoqKmstbWVhbnMgY2x1c3RlciBrPTMqKg0KDQpgYGB7cn0NCiNzZXQgYSBzZWVkIGZvciByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gIHRvIG1ha2UgdGhlIHJlc3VsdHMgcmVwcm9kdWNpYmxlDQpzZXQuc2VlZCg3KQ0Ka21lYW5zLnJlc3VsdCA8LSBrbWVhbnMoSG9iYnlfRGF0YTIsIDMpDQoNCiMgcHJpbnQgdGhlIGNsdXN0ZXJuZyByZXN1bHQNCmttZWFucy5yZXN1bHQNCg0KI3Zpc3VhbGl6ZSBjbHVzdGVyaW5nDQpmdml6X2NsdXN0ZXIoa21lYW5zLnJlc3VsdCwgZGF0YSA9IEhvYmJ5X0RhdGEyKQ0KYGBgDQoNCioqQXZlcmFnZSBmb3IgZWFjaCBjbHVzdGVyKioNCg0KYGBge3J9DQphdmdfc2lsIDwtIHNpbGhvdWV0dGUoa21lYW5zLnJlc3VsdCRjbHVzdGVyLGRpc3QoSG9iYnlfRGF0YTIpKSANCmZ2aXpfc2lsaG91ZXR0ZShhdmdfc2lsKQ0KYGBgDQoNCnRoZSBwcmVzZW5jZSBvZiBuZWdhdGl2ZSBzaWxob3VldHRlcyBmb3Igc29tZSBvYnNlcnZhdGlvbnMgd2l0aGluIGVhY2gNCmNsdXN0ZXIgaW5kaWNhdGVzIHRoYXQgdGhlc2UgcG9pbnRzIG1pZ2h0IGJlIG1vcmUgc2ltaWxhciB0byBwb2ludHMgaW4NCm90aGVyIGNsdXN0ZXJzLCBzdWdnZXN0aW5nIGEgcG90ZW50aWFsIG92ZXJsYXAgb3IgYW1iaWd1aXR5IGluIHRoZWlyDQphc3NpZ25tZW50LiBUaGUgZmFjdCB0aGF0IHNvbWUgb2JzZXJ2YXRpb25zIGhhdmUgbmVnYXRpdmUgc2lsaG91ZXR0ZXMNCmhpZ2hsaWdodHMgdGhhdCB0aGUgc2VwYXJhdGlvbiBvZiBkYXRhIHBvaW50cyBpcyBub3QgZW50aXJlbHkNCnN1ZmZpY2llbnQuDQoNClNvIHNpbmNlIDMsIHdoaWNoIHdhcyB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgd2l0aCB0aGUgaGlnaGVzdA0Kc2lsaG91ZXR0ZSBzY29yZSBhdmVyYWdlLCBkaWQgbm90IGhhdmUgZ29vZCBjbHVzdGVyaW5nIHJlc3VsdHMsIHRoYXQNCmRvZXMgc3VwcG9ydCBvdXIgcmVzZWFyY2ggcmVzdWx0cyB0aGF0IHRoZSBrLW1lYW5zIGFsZ29yaXRobSBpcyBub3QNCmFwcGxpY2FibGUgdG8gY2F0ZWdvcmljYWxkYXRhIGNsdXN0ZXJpbmcgYmVjYXVzZSBpdCByZWxpZXMgb24gdGhlDQpFdWNsaWRlYW4gZGlzdGFuY2UgbWV0cmljIHRvbWVhc3VyZSB0aGUgc2ltaWxhcml0eSBiZXR3ZWVuIGRhdGEgcG9pbnRzLg0KSG93ZXZlciwgZXZlbiBhZnRlckVuY29kaW5nIGFuZCBpdHMgYXBwbGljYXRpb24gdG8gY2F0ZWdvcmljYWwgZGF0YSBwb3NlDQpzaWduaWZpY2FudCBjaGFsbGVuZ2VzLiBDYXRlZ29yaWNhbCB2YXJpYWJsZXMgb2Z0ZW4gbGFjayBhIG1lYW5pbmdmdWwNCm51bWVyaWNhbHJlcHJlc2VudGF0aW9uOyBmb3IgaW5zdGFuY2UsIHRha2luZyB0aGUgbWVhbiBvZiBjYXRlZ29yaWVzDQpsaWtlIHRoZWZlYXR1cmUgImZhdm9yaXRlIHN1YmplY3QsIiBha2EgRmF2X3N1YiIgKGV2ZW4gYWZ0ZXIgZW5jb2RpbmcpLA0KbWlnaHQgbm90aGF2ZSBhbnkgcHJhY3RpY2FsIGludGVycHJldGF0aW9uLiBBbmQgdGhlIGRpc3RhbmNlcyBjYWxjdWxhdGVkDQppbiB0aGVhbGdvcml0aG0gbWF5IG5vdCByZWZsZWN0IHRoZSB0cnVlIGRpc3NpbWlsYXJpdGllcyBiZXR3ZWVuDQpjYXRlZ29yaWNhbHZhbHVlcy4gVGhlIGVuY29kaW5nIHByb2Nlc3MgaXRzZWxmIGludHJvZHVjZXMgYXJ0aWZpY2lhbA0KbnVtZXJpY2FscmVsYXRpb25zaGlwcyB0aGF0IG1heSBtaXNsZWFkIHRoZSBhbGdvcml0aG0uIE1vcmVvdmVyLCBrLW1lYW5zDQpyZWxpZXNvbiB0aGUgbWluaW1pemF0aW9uIG9mIEV1Y2xpZGVhbiBkaXN0YW5jZXMsIHdoaWNoIG1pZ2h0IG5vdA0KYWNjdXJhdGVseWNhcHR1cmUgdGhlIGRpc3NpbWlsYXJpdHkgc3RydWN0dXJlIGluIGNhdGVnb3JpY2FsIGRhdGEuDQpDYXRlZ29yaWNhbHZhcmlhYmxlcyBpbmhlcmVudGx5IGV4aGliaXQgZGlzY3JldGUgYW5kIG5vbi1vcmRpbmFsDQpjaGFyYWN0ZXJpc3RpY3N0aGF0IGFyZSBub3Qgd2VsbC1zdWl0ZWQgZm9yIHRoZSBjb250aW51b3VzIGFuZCBsaW5lYXINCmFzc3VtcHRpb25zIG9may1tZWFucy4gQWx0ZXJuYXRpdmUgY2x1c3RlcmluZyB0ZWNobmlxdWVzLCBzcGVjaWZpY2FsbHkNCmRlc2lnbmVkIGZvcmNhdGVnb3JpY2FsIGRhdGEsIHN1Y2ggYXMgcGFydGl0aW9uaW5nIGFyb3VuZCBtZWRvaWRzLCBhcmUNCm1vcmVhcHByb3ByaWF0ZSBmb3IgY2FwdHVyaW5nIHRoZSBpbnRyaW5zaWMgcGF0dGVybnMgYW5kIHJlbGF0aW9uc2hpcHMNCmluY2F0ZWdvcmljYWwgZGF0YXNldHMuDQoNCg0KDQoNCg0KDQoNCioqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1rLW1lZGlvZHMgY2x1c3RlcmluZyB3aXRoIFBBTS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KSy1tZWRvaWRzIGNsdXN0ZXJpbmcgcHJlc2VudHMgYSByb2J1c3QgYWx0ZXJuYXRpdmUgZm9yIGFuYWx5emluZw0KY2F0ZWdvcmljYWwgZGF0YSBieSBhZGRyZXNzaW5nIHRoZSBsaW1pdGF0aW9ucyBwb3NlZCBieSBrLW1lYW5zDQpjbHVzdGVyaW5nLiBVbmxpa2Ugay1tZWFucywgay1tZWRvaWRzIGRvZXMgbm90IHJlbHkgb24gdGhlIG1lYW4gYXMgYQ0KcmVwcmVzZW50YXRpdmUgY2VudHJvaWQgYnV0IGVtcGxveXMgbWVkb2lkcywgd2hpY2ggYXJlIGFjdHVhbCBkYXRhDQpwb2ludHMgd2l0aGluIHRoZSBjbHVzdGVycywgYW5kIHRoZSBhbGdvcml0aG0gZGVmaW5lcyBjbHVzdGVycyBiYXNlZCBvbg0KcGFydGl0aW9uaW5nIGFyb3VuZCBtZWRvaWRzLiBUaGlzIGZlYXR1cmUgbWFrZXMgay1tZWRvaWRzIHBhcnRpY3VsYXJseQ0Kc3VpdGFibGUgZm9yIGNhdGVnb3JpY2FsIGRhdGEsIHdoZXJlIG1lYW5pbmdmdWwgY2VudHJvaWRzIG1heSBub3QgaGF2ZSBhDQpudW1lcmljYWwgaW50ZXJwcmV0YXRpb24uIFRoZSBhbGdvcml0aG0gZGVmaW5lcyBjbHVzdGVycyBiYXNlZCBvbg0KcGFydGl0aW9uaW5nIGFyb3VuZCBtZWRvaWRzLg0KDQoqKlZhbGlkYXRpb246KioNCg0KRmlyc3QsIHdlIHdhbnQgdG8gZGV0ZXJtaW5lIHRocmVlIGRpZmZlcmVudCBudW1iZXJzIG9mIGNsdXN0ZXJzIGJ5IHVzaW5nDQphIG51bWJlciBvZiBtZXRob2RzIHRoYXQgd2lsbCBzdWdnZXN0IHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyBmb3INCmstbWVkaW9kcyBjbHVzdGVyaW5nIHdpdGggUEFNLg0KDQoqKlNpbGhvdWV0dGUgY29lZmZpY2llbnQqKg0KDQpgYGB7cn0NCmZ2aXpfbmJjbHVzdChIb2JieV9EYXRhMiwgcGFtLCBtZXRob2QgPSAic2lsaG91ZXR0ZSIpKw0KICBsYWJzKHN1YnRpdGxlID0gIlNpbGhvdWV0dGUgbWV0aG9kIikNCmBgYA0KDQoqKkVsYm93IG1ldGhvZCoqDQoNCmBgYHtyfQ0KZnZpel9uYmNsdXN0KEhvYmJ5X0RhdGEyLCBwYW0sIG1ldGhvZCA9ICJ3c3MiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdD0gMywgbGluZXR5cGU9IDMpKw0KICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpDQpgYGANCg0KVGhlIHNpbGhvdWV0dGUgY29lZmZpY2llbnQsIHdoaWNoIG1lYXN1cmVzIHRoZSBjb2hlc2lvbiBhbmQgc2VwYXJhdGlvbg0Kb2YgY2x1c3RlcnMsIGFsaWducyB3aXRoIHRoZSBFbGJvdyBtZXRob2QsIHdoaWNoIGFzc2Vzc2VzIHRoZQ0Kd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMgKHdzcykgYXMgYSBmdW5jdGlvbiBvZiB0aGUgbnVtYmVyIG9mDQpjbHVzdGVycyBpbiB0aGUgc3VnZ2VzdGVkIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzLiBUaGlzIGFsaWdubWVudCBpbg0KcmVzdWx0cyBiZXR3ZWVuIHR3byBkaXN0aW5jdCBldmFsdWF0aW9uIG1ldGhvZHMgc3RyZW5ndGhlbnMgY29uZmlkZW5jZQ0KaW4gdGhlIGNob2ljZSBvZiB0aHJlZSBjbHVzdGVycywgcHJvdmlkaW5nIGEgc3RhYmxlIGZvdW5kYXRpb24gZm9yDQpmdXJ0aGVyIGFuYWx5c2lzIGFuZCBpbnRlcnByZXRhdGlvbiBvZiB0aGUgdW5kZXJseWluZyBwYXR0ZXJucyB3aXRoaW4NCnRoZSBkYXRhc2V0LiBJbiBhZGRpdGlvbiB0byB0aGF0LCB0aGUgZ3JvdW5kIHRydXRoIChjbGFzcyBsYWJlbHMpIGFsc28NCmNvbnRhaW5zIHRocmVlIGNsYXNzZXMsIGFuZCB0aGF0IGluZGljYXRlcyBhIHJlYXNzdXJpbmcgYWxpZ25tZW50DQpiZXR3ZWVuIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgYW5kIHRoZSBjbHVzdGVyaW5nIHJlc3VsdHMuDQoNCg0KDQpXZSBuZWVkIHRvIGRldGVybWluZSB0d28gbW9yZSBzdWdnZXN0ZWQgbnVtYmVycyBvZiBjbHVzdGVycyBieSBjb21wdXRpbmcNCnRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgZm9yIGsgY2x1c3RlcnMgdXNpbmcgc2lsaG91ZXR0ZSgpLg0KYGBge3J9DQpzaWxob3VldHRlX3Njb3JlIDwtIGZ1bmN0aW9uKGspIHsNCiAga20gPC0gcGFtKEhvYmJ5X0RhdGEyLCBrLCBkaXNzID0gVFJVRSkNCiAgc3MgPC0gc2lsaG91ZXR0ZShrbSRjbHVzdGVyaW5nLCBkaXN0KEhvYmJ5X0RhdGEyKSkNCiAgc2lsIDwtIG1lYW4oc3NbLCAzXSkNCiAgcmV0dXJuKHNpbCkNCn0NCg0KIyBrIGNsdXN0ZXIgcmFuZ2UgZnJvbSAyIHRvIDEwDQprIDwtIDI6MTANCg0KIyBDYWxsIGZ1bmN0aW9uIGZvciBlYWNoIGsgdmFsdWUNCmF2Z19zaWwgPC0gc2FwcGx5KGssIHNpbGhvdWV0dGVfc2NvcmUpDQoNCiMgUGxvdCB0aGUgcmVzdWx0cw0KcGxvdChrLCBhdmdfc2lsLCB0eXBlID0gJ2InLCB4bGFiID0gJ051bWJlciBvZiBjbHVzdGVycycsIHlsYWIgPSAnQXZlcmFnZSBTaWxob3VldHRlIFNjb3JlcycsIGZyYW1lID0gRkFMU0UpDQpgYGANCg0KSXQgaXMgYSBjb21tb24gcHJhY3RpY2UgdG8gY2hvb3NlIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgY29ycmVzcG9uZGluZw0KdG8gdGhlIHBlYWsgaW4gdGhlIHNpbGhvdWV0dGUgc2NvcmUgcGxvdCwgYW5kIHNpbmNlIHdlIGFyZSBsb29raW5nIGZvcg0KdHdvIG1vcmUgbnVtYmVyIG9mIGNsdXN0ZXJzIG90aGVyIHRoYW4gdGhyZWUsIGl0IHdvdWxkIGJlIHJlYXNvbmFibGUgdG8NCmNvbnNpZGVyIHR3byBhbmQgZm91ciBjbHVzdGVycyBmb3IgZnVydGhlciBhbmFseXNpcy4gRXNwZWNpYWxseSB3aXRoIHRoZQ0KZGVjcmVhc2luZyB0cmVuZCBiZXlvbmQgdGhyZWUgY2x1c3RlcnMsIGl0IGluZGljYXRlcyB0aGF0IGFkZGluZyBtb3JlDQpjbHVzdGVycyBkb2VzIG5vdCBzaWduaWZpY2FudGx5IGltcHJvdmUgdGhlIHNlcGFyYXRpb24gYW5kIGNvaGVzaW9uIG9mDQp0aGUgY2x1c3RlcnMuDQoNCg0KDQoqKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tZ3JvdXAgaW50byBrPTMgY2x1c3RlcnMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSoqDQoNClRoZSBzdWItc2FtcGxpbmcgYW5kIGNsdXN0ZXJpbmcgYXBwcm9hY2ggaXMgYSBoZWxwZnVsIG1ldGhvZCB0byBldmFsdWF0ZQ0KdGhlIHJvYnVzdG5lc3Mgb2YgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyB1bmRlciB2YXJpb3VzIHN1YnNldHMgYW5kIHRvDQpvYnRhaW4gaW5zaWdodHMgaW50byB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhLiBGdXJ0aGVybW9yZSwgd2Ugd2lsbA0KdGFrZSAxMDAgc2FtcGxlcyBmcm9tIG91ciBkYXRhIHNldCB0byBlbnN1cmUgdGhhdCB0aGUgY2x1c3RlcmluZyBwbG90DQpkb2Vzbid0IGdldCB0b28gY3Jvd2RlZC4NCg0KYGBge3J9DQpzZXQuc2VlZCg3KQ0KDQojIFNwZWNpZnkgdGhlIG51bWJlciBvZiByb3dzIHlvdSB3YW50IHRvIHNhbXBsZQ0KbnVtX3Jvd3MgPC0gMTAwDQoNCiMgVXNlIHNhbXBsZSB3aXRoIHRoZSBzcGVjaWZpZWQgc2VlZA0KaWR4IDwtIHNhbXBsZSgxOmRpbShIb2JieV9EYXRhMilbMV0sIG51bV9yb3dzKQ0KDQpIb2JieV9EYXRhMyA8LSBIb2JieV9EYXRhMltpZHgsIF0NCg0KcGFtLnJlc3VsdCA8LSBwYW0oSG9iYnlfRGF0YTMsMykNCiNTaG93IHRoZSBzaWxob3V0ZWUgcGxvdCBvZiBQQU0gQU5EIGNsdXN0ZXJzDQpwbG90KHBhbS5yZXN1bHQpDQpgYGANCg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIGNsdXNpbmZvIGNvbXBvbmVudA0KY2x1c2luZm8gPC0gcGFtLnJlc3VsdCRjbHVzaW5mbw0KDQojIENhbGN1bGF0ZSB0aGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMNCnRvdF93aXRoaW5zcyA8LSBzdW0oY2x1c2luZm9bLCAic2l6ZSJdICogY2x1c2luZm9bLCAiYXZfZGlzcyJdXjIpDQoNCiMgUHJpbnQgdGhlIHJlc3VsdA0KcHJpbnQodG90X3dpdGhpbnNzKQ0KYGBgDQoNCg0KYGBge3J9DQpzdHIoSG9iYnlfRGF0YTIpDQpgYGANCg0KKipEYXRhIHNldCB3aXRob3V0IGdyb3VuZCB0cnV0aCoqDQoNCmBgYHtyfQ0KSG9iYnlfRGF0YTIgPC0gSG9iYnlfcHJvY1ssICEoY29sbmFtZXMoSG9iYnlfcHJvYykgJWluJSBjKCJQcmVkaWN0ZWQgSG9iYnkiKSldDQpgYGANCg0KDQpgYGB7cn0NCiMgQ2hlY2sgdGhlIHN0cnVjdHVyZSBvZiBwYW0ucmVzdWx0JGNsdXN0ZXJpbmcNCnN0cihwYW0ucmVzdWx0JGNsdXN0ZXJpbmcpDQoNCnN1bW1hcnkoSG9iYnlfRGF0YTIpDQpgYGANCg0KDQpgYGB7cn0NCnN0cihIb2JieV9wcm9jKQ0KDQpzdHIoSG9iYnlfRGF0YTIpDQpgYGANCg0KDQpgYGB7cn0NCg0KbGlicmFyeShHR2FsbHkpDQpsaWJyYXJ5KHBsb3RseSkNCg0KDQojIEFzc3VtaW5nICdwYW0ucmVzdWx0JyBpcyB0aGUgcmVzdWx0IGZyb20gUEFNIGNsdXN0ZXJpbmcNCkhvYmJ5X0RhdGEyJGNsdXN0ZXIgPC0gcGFtLnJlc3VsdCRjbHVzdGVyaW5nDQoNCiMgTWFrZSBzdXJlICdjbHVzdGVyJyBpcyBhIGZhY3Rvcg0KSG9iYnlfRGF0YTIkY2x1c3RlciA8LSBhcy5mYWN0b3IoSG9iYnlfRGF0YTIkY2x1c3RlcikNCg0KIyBDcmVhdGUgYSBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QNCnAgPC0gZ2dwYXJjb29yZCgNCiAgZGF0YSA9IEhvYmJ5X0RhdGEyLA0KICBjb2x1bW5zID0gYygxOjExKSwgICMgQXNzdW1pbmcgdGhlcmUgYXJlIDExIGNvbHVtbnMgaW4geW91ciBkYXRhDQogIGdyb3VwQ29sdW1uID0gImNsdXN0ZXIiLA0KICBzY2FsZSA9ICJzdGQiDQopICsgbGFicyh4ID0gIkhvYmJ5IGNoYXJhY3RlcmlzdGljIiwgeSA9ICJ2YWx1ZSAoaW4gc3RhbmRhcmQtZGV2aWF0aW9uIHVuaXRzKSIsIHRpdGxlID0gIkNsdXN0ZXJpbmciKQ0KDQojIENvbnZlcnQgdGhlIGdncGxvdCBvYmplY3QgdG8gcGxvdGx5DQpwX3Bsb3RseSA8LSBnZ3Bsb3RseShwKQ0KDQojIFByaW50IHRoZSBwbG90DQpwcmludChwX3Bsb3RseSkNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShwbG90bHkpDQoNCkhvYmJ5X0RhdGEyJGNsdXN0ZXIgPC0gYXMuZmFjdG9yKHBhbS5yZXN1bHQkY2x1c3RlcikNCg0KcCA8LSBnZ3BhcmNvb3JkKGRhdGEgPSBIb2JieV9EYXRhMiwgY29sdW1ucyA9IGMoMToxMSksIGdyb3VwQ29sdW1uID0gImNsdXN0ZXIiLCBzY2FsZSA9ICJzdGQiKSArIGxhYnMoeCA9ICJtaWxrIGNvbnN0aXR1ZW50IiwgeSA9ICJ2YWx1ZSAoaW4gc3RhbmRhcmQtZGV2aWF0aW9uIHVuaXRzKSIsIHRpdGxlID0gIkNsdXN0ZXJpbmciKQ0KZ2dwbG90bHkocCkNCg0KYGBgDQoNClRoZSBvdXRwdXQgb2YgdGhlIGNvZGUsIGRpc3BsYXlpbmcgYSBzaWxob3VldHRlIHBsb3Qgb2YgdGhlIFBBTQ0KY2x1c3RlcnMsIGluZGljYXRlcyB0aGF0IHRoZSBjbHVzdGVycyBhcmUgcmVsYXRpdmVseSBjbG9zZSB0byBlYWNoDQpvdGhlciwgd2l0aCBhIHNsaWdodCBvdmVybGFwIGJldHdlZW4gdHdvIGNsdXN0ZXJzLiBUaGUgc2lsaG91ZXR0ZSBwbG90DQp2aXN1YWxseSByZXByZXNlbnRzIGhvdyB3ZWxsLWRlZmluZWQgYW5kIHNlcGFyYXRlZCB0aGUgY2x1c3RlcnMgYXJlLg0KV2hpbGUgdGhlIHNsaWdodCBvdmVybGFwIHN1Z2dlc3RzIHRoYXQgdGhlIG5hdHVyYWwgZ3JvdXBpbmcgd2l0aGluIHRoZQ0KZGF0YXNldCBtYXkgbm90IGJlIGVudGlyZWx5IGRpc3RpbmN0LCBpdCBkb2VzIG5vdCBuZWdhdGl2ZWx5IGFmZmVjdCB0aGUNCm92ZXJhbGwgcXVhbGl0eSBvZiB0aGUgY2x1c3RlcnMuIEluIGZhY3QsIHRoZSBvYnNlcnZlZCBvdmVybGFwIG1pZ2h0DQppbmRpY2F0ZSBzaGFyZWQgY2hhcmFjdGVyaXN0aWNzIGJldHdlZW4gYWRqYWNlbnQgY2x1c3RlcnMsIGVmZmVjdGl2ZWx5DQpjYXB0dXJpbmcgbWVhbmluZ2Z1bCBwYXR0ZXJucyBhbmQgZ3JvdXBpbmdzIHRoYXQgcmVmbGVjdCB0aGUgaW50cmljYWNpZXMNCm9mIHJlYWwtd29ybGQgcGhlbm9tZW5hIG5vdCBjb25maW5lZCB0byBzdHJpY3QgYm91bmRhcmllcy4NCg0KYkN1YmVkDQoNCmBgYHtyfQ0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBjKHBhbS5yZXN1bHQkY2x1c3RlcikNCnNldC5zZWVkKDcpDQoNCiMgU3BlY2lmeSB0aGUgbnVtYmVyIG9mIHJvd3MgeW91IHdhbnQgdG8gc2FtcGxlDQpudW1fcm93cyA8LSAxMDANCg0KIyBVc2Ugc2FtcGxlIHdpdGggdGhlIHNwZWNpZmllZCBzZWVkDQppZHggPC0gc2FtcGxlKDE6ZGltKEhvYmJ5X3Byb2MpWzFdLCBudW1fcm93cykNCg0KIyBTZWxlY3QgdGhlIHNhbXBsZWQgcm93cyBmcm9tIEhvYmJ5X3Byb2MNCkhvYmJ5X0RhdGE0IDwtIEhvYmJ5X3Byb2NbaWR4LCBdDQpncm91bmRfdHJ1dGhfbGFiZWxzIDwtIGMoSG9iYnlfRGF0YTQpDQoNCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIGNsdXN0ZXIgYXNzaWdubWVudHMgYW5kIGdyb3VuZCB0cnV0aCBsYWJlbHMNCmRhdGFzZXQgPC0gZGF0YS5mcmFtZShjbHVzdGVyID0gY2x1c3Rlcl9hc3NpZ25tZW50cywgbGFiZWwgPSBncm91bmRfdHJ1dGhfbGFiZWxzKQ0KDQojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwNCmNhbGN1bGF0ZV9iY3ViZWRfbWV0cmljcyA8LSBmdW5jdGlvbihkYXRhc2V0KSB7DQogIG4gPC0gbnJvdyhkYXRhc2V0KQ0KICBwcmVjaXNpb25fc3VtIDwtIDAgDQogIHJlY2FsbF9zdW0gPC0gMA0KICANCiAgIGZvciAoaSBpbiAxOm4pIHsNCiAgICBjbHVzdGVyIDwtIGRhdGFzZXQkY2x1c3RlcltpXSANCiAgICBsYWJlbCA8LSBkYXRhc2V0JGxhYmVsW2ldDQogICAgDQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGZyb20gdGhlIHNhbWUgY2F0ZWdvcnkgaW4gaXRzIGNsdXN0ZXINCiAgICBzYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsW2RhdGFzZXQkY2x1c3RlciA9PSBjbHVzdGVyXSA9PSBsYWJlbCkgICANCiAgICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gaXRzIGNsdXN0ZXIgICAgDQogICAgc2FtZV9jbHVzdGVyIDwtIHN1bShkYXRhc2V0JGNsdXN0ZXIgPT0gY2x1c3RlcikNCiAgICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gaXRzIGNhdGVnb3J5DQogICAgdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YXNldCRsYWJlbCA9PSBsYWJlbCkgICANCiAgICANCiAgICAjIENhbGN1bGF0ZSBwcmVjaXNpb24gYW5kIHJlY2FsbCANCiAgICBwcmVjaXNpb25fc3VtIDwtIHByZWNpc2lvbl9zdW0gKyBzYW1lX2NhdGVnb3J5IC8gc2FtZV9jbHVzdGVyDQogICAgcmVjYWxsX3N1bSA8LSByZWNhbGxfc3VtICsgc2FtZV9jYXRlZ29yeSAvIHRvdGFsX3NhbWVfY2F0ZWdvcnkgDQogICAgfQ0KICAjIEVuZCBsb29wIA0KICANCiAgIyBDYWxjdWxhdGUgYXZlcmFnZSBwcmVjaXNpb24gYW5kIHJlY2FsbCAgDQogIHByZWNpc2lvbiA8LSBwcmVjaXNpb25fc3VtIC8gbg0KICByZWNhbGwgPC0gcmVjYWxsX3N1bSAvIG4gDQogIHJldHVybihsaXN0KHByZWNpc2lvbiA9IHByZWNpc2lvbiwgcmVjYWxsID0gcmVjYWxsKSl9DQoNCiAgIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogIG1ldHJpY3MgPC0gY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzKGRhdGFzZXQpDQogIHByZWNpc2lvbiA8LSBtZXRyaWNzJHByZWNpc2lvbg0KICByZWNhbGwgPC0gbWV0cmljcyRyZWNhbGwNCg0KIyBQcmludCB0aGUgcmVzdWx0cw0KICBjYXQoIkJDdWJlZCBQcmVjaXNpb249ICIsIHByZWNpc2lvbiwgIkFORCBCQ3ViZWQgUmVjYWxsPSAiLCByZWNhbGwsICJcbiIpDQpgYGANCg0KV2hpbGUgcHJlY2lzaW9uIGhpZ2hsaWdodHMgcm9vbSBmb3IgYmV0dGVyIGFjY3VyYWN5IGluIGlkZW50aWZ5aW5nDQpzaW1pbGFyIGl0ZW1zLCB0aGUgaGlnaGVyIHJlY2FsbCBpbmRpY2F0ZXMgdGhlIGFsZ29yaXRobSdzIGNhcGFiaWxpdHkgdG8NCmNhdGNoIGEgZ29vZCBhbW91bnQgb2YgYWN0dWFsIHNpbWlsYXJpdGllcyB3aXRoaW4gY2x1c3RlcnMuDQoNCg0KDQoNCioqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1ncm91cCBpbnRvIGs9NCBjbHVzdGVycy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSoqDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNykNCg0KIyBTcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB5b3Ugd2FudCB0byBzYW1wbGUNCm51bV9yb3dzIDwtIDEwMA0KDQojIFVzZSBzYW1wbGUgd2l0aCB0aGUgc3BlY2lmaWVkIHNlZWQNCmlkeCA8LSBzYW1wbGUoMTpkaW0oSG9iYnlfRGF0YTIpWzFdLCBudW1fcm93cykNCg0KSG9iYnlfRGF0YTMgPC0gSG9iYnlfRGF0YTJbaWR4LCBdDQoNCnBhbS5yZXN1bHQgPC0gcGFtKEhvYmJ5X0RhdGEzLDQpDQojU2hvdyB0aGUgc2lsaG91dGVlIHBsb3Qgb2YgUEFNIEFORCBjbHVzdGVycw0KcGxvdChwYW0ucmVzdWx0KQ0KYGBgDQoNCg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIGNsdXNpbmZvIGNvbXBvbmVudA0KY2x1c2luZm8gPC0gcGFtLnJlc3VsdCRjbHVzaW5mbw0KDQojIENhbGN1bGF0ZSB0aGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMNCnRvdF93aXRoaW5zcyA8LSBzdW0oY2x1c2luZm9bLCAic2l6ZSJdICogY2x1c2luZm9bLCAiYXZfZGlzcyJdXjIpDQoNCiMgUHJpbnQgdGhlIHJlc3VsdA0KcHJpbnQodG90X3dpdGhpbnNzKQ0KYGBgDQpUaGUgb3V0cHV0IHN1Z2dlc3RzIHRoYXQgdGhlIGRhdGFzZXQgbWF5IGV4aGliaXQgYSBkZWdyZWUgb2Ygb3ZlcmxhcCBvcg0Kc2ltaWxhcml0eSBhbW9uZyBvYnNlcnZhdGlvbnMuIFRoZSBvdmVybGFwcGluZyBjbHVzdGVycyBtYXkgaW5kaWNhdGUNCmNoYWxsZW5nZXMgaW4gYWNoaWV2aW5nIGEgY2xlYXIgc2VwYXJhdGlvbiBhbW9uZyB0aGVzZSBncm91cHMuIFRoZQ0KcGxhY2VtZW50IG9mIG9uZSBjbHVzdGVyIG9uIHRvcCBvZiB0d28gb3RoZXJzIGltcGxpZXMgdGhhdCB0aGUgbWVkb2lkIG9mDQp0aGlzIGNsdXN0ZXIgbWlnaHQgYmUgbmVhciBwb2ludHMgYmVsb25naW5nIHRvIHRob3NlIHR3byBuZWlnaGJvcmluZw0KY2x1c3RlcnMuIFRoaXMgY291bGQgYmUgZHVlIHRvIHRoZSBuYXR1cmUgb2YgdGhlIGRhdGEuDQoNCmBgYHtyfQ0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBwYW0ucmVzdWx0JGNsdXN0ZXJpbmcNCg0KIyBBZGQgY2x1c3RlciBhc3NpZ25tZW50cyB0byB0aGUgb3JpZ2luYWwgZGF0YQ0KSG9iYnlfRGF0YV93aXRoX2NsdXN0ZXJzIDwtIGNiaW5kKEhvYmJ5X0RhdGEzLCBDbHVzdGVyID0gZmFjdG9yKGNsdXN0ZXJfYXNzaWdubWVudHMpKQ0KDQojIEV4YW1wbGU6IFBhaXIgcGxvdCBmb3IgdGhlIGZpcnN0IGZvdXIgZmVhdHVyZXMNCnBhaXJzKEhvYmJ5X0RhdGFfd2l0aF9jbHVzdGVyc1ssIDE6NF0sIGNvbCA9IGNsdXN0ZXJfYXNzaWdubWVudHMpDQoNCg0KDQpgYGANCg0KDQoNCg0KDQpCQ3ViZWQNCmBgYHtyfQ0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBjKHBhbS5yZXN1bHQkY2x1c3RlcikgDQogDQpzZXQuc2VlZCg3KSANCiANCiMgU3BlY2lmeSB0aGUgbnVtYmVyIG9mIHJvd3MgeW91IHdhbnQgdG8gc2FtcGxlIA0KbnVtX3Jvd3MgPC0gMTAwIA0KIA0KIyBVc2Ugc2FtcGxlIHdpdGggdGhlIHNwZWNpZmllZCBzZWVkIA0KaWR4IDwtIHNhbXBsZSgxOmRpbShIb2JieV9wcm9jKVsxXSwgbnVtX3Jvd3MpIA0KIA0KIyBTZWxlY3QgdGhlIHNhbXBsZWQgcm93cyBmcm9tIEhvYmJ5X3Byb2MgDQpIb2JieV9EYXRhNCA8LSBIb2JieV9wcm9jW2lkeCwgXSANCg0KIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggY2x1c3RlciBhc3NpZ25tZW50cyBhbmQgZ3JvdW5kIHRydXRoIGxhYmVscw0KZGF0YXNldCA8LSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBjbHVzdGVyX2Fzc2lnbm1lbnRzLCBsYWJlbCA9IGdyb3VuZF90cnV0aF9sYWJlbHMpDQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzIDwtIGZ1bmN0aW9uKGRhdGFzZXQpIHsNCiAgbiA8LSBucm93KGRhdGFzZXQpDQogIHByZWNpc2lvbl9zdW0gPC0gMA0KICByZWNhbGxfc3VtIDwtIDANCiANCiAgZm9yIChpIGluIDE6bikgew0KICAgIGNsdXN0ZXIgPC0gZGF0YXNldCRjbHVzdGVyW2ldDQogICAgbGFiZWwgPC0gZGF0YXNldCRsYWJlbFtpXQ0KICAgDQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGZyb20gdGhlIHNhbWUgY2F0ZWdvcnkgaW4gaXRzIGNsdXN0ZXINCiAgICBzYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsW2RhdGFzZXQkY2x1c3RlciA9PSBjbHVzdGVyXSA9PSBsYWJlbCkNCiAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2x1c3Rlcg0KICAgIHNhbWVfY2x1c3RlciA8LSBzdW0oZGF0YXNldCRjbHVzdGVyID09IGNsdXN0ZXIpDQogICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gaXRzIGNhdGVnb3J5DQogICAgdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YXNldCRsYWJlbCA9PSBsYWJlbCkNCiAgIA0KICAgICMgQ2FsY3VsYXRlIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogICAgcHJlY2lzaW9uX3N1bSA8LSBwcmVjaXNpb25fc3VtICsgc2FtZV9jYXRlZ29yeSAvIHNhbWVfY2x1c3Rlcg0KICAgIHJlY2FsbF9zdW0gPC0gcmVjYWxsX3N1bSArIHNhbWVfY2F0ZWdvcnkgLyB0b3RhbF9zYW1lX2NhdGVnb3J5DQogIH0NCiAgIyBFbmQgbG9vcA0KIA0KICAjIENhbGN1bGF0ZSBhdmVyYWdlIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogIHByZWNpc2lvbiA8LSBwcmVjaXNpb25fc3VtIC8gbg0KICByZWNhbGwgPC0gcmVjYWxsX3N1bSAvIG4NCiANCiAgcmV0dXJuKGxpc3QocHJlY2lzaW9uID0gcHJlY2lzaW9uLCByZWNhbGwgPSByZWNhbGwpKQ0KfQ0KDQojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwNCm1ldHJpY3MgPC0gY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzKGRhdGFzZXQpDQpwcmVjaXNpb24gPC0gbWV0cmljcyRwcmVjaXNpb24NCnJlY2FsbCA8LSBtZXRyaWNzJHJlY2FsbA0KDQojIFByaW50IHRoZSByZXN1bHRzDQpjYXQoIkJDdWJlZCBQcmVjaXNpb249ICIsIHByZWNpc2lvbiwgIkFORCBCQ3ViZWQgUmVjYWxsPSAiLCByZWNhbGwsICJcbiIpDQpgYGANCg0KVGhlIHJlc3VsdHMgaW5kaWNhdGUgY2hhbGxlbmdlcyBpbiBjbHVzdGVyaW5nIHBlcmZvcm1hbmNlLiBUaGUgbG93DQpwcmVjaXNpb24gc3VnZ2VzdHMgYSBzaWduaWZpY2FudCByYXRlIG9mIG1pc2NsYXNzaWZpY2F0aW9uLCB3aGlsZSB0aGUNCnJlbGF0aXZlbHkgbG93IHJlY2FsbCBpbmRpY2F0ZXMgdGhhdCBzb21lIGluc3RhbmNlcyB3aXRoaW4gdGhlIHNhbWUNCmdyb3VwIGFyZSBtaXNzZWQgb3IgaW5jb3JyZWN0bHkgYXNzaWduZWQgdG8gb3RoZXIgY2x1c3RlcnMuIFRoZXNlDQpyZXN1bHRzIGhpZ2hsaWdodCBsaW1pdGF0aW9ucyBpbiBhY2N1cmF0ZWx5IGNhcHR1cmluZyB0aGUgZGF0YSdzDQp1bmRlcmx5aW5nIHN0cnVjdHVyZS4NCg0KDQoNCioqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLWdyb3VwIGludG8gaz0yIGNsdXN0ZXJzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qKg0KDQpgYGB7cn0NCnNldC5zZWVkKDcpDQoNCiMgU3BlY2lmeSB0aGUgbnVtYmVyIG9mIHJvd3MgeW91IHdhbnQgdG8gc2FtcGxlDQpudW1fcm93cyA8LSAxMDANCg0KIyBVc2Ugc2FtcGxlIHdpdGggdGhlIHNwZWNpZmllZCBzZWVkDQppZHggPC0gc2FtcGxlKDE6ZGltKEhvYmJ5X0RhdGEyKVsxXSwgbnVtX3Jvd3MpDQoNCkhvYmJ5X0RhdGEzIDwtIEhvYmJ5X0RhdGEyW2lkeCwgXQ0KDQpwYW0ucmVzdWx0IDwtIHBhbShIb2JieV9EYXRhMywyKQ0KI1Nob3cgdGhlIHNpbGhvdXRlZSBwbG90IG9mIFBBTSBBTkQgY2x1c3RlcnMNCnBsb3QocGFtLnJlc3VsdCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IHRoZSBjbHVzaW5mbyBjb21wb25lbnQNCmNsdXNpbmZvIDwtIHBhbS5yZXN1bHQkY2x1c2luZm8NCg0KIyBDYWxjdWxhdGUgdGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzDQp0b3Rfd2l0aGluc3MgPC0gc3VtKGNsdXNpbmZvWywgInNpemUiXSAqIGNsdXNpbmZvWywgImF2X2Rpc3MiXV4yKQ0KDQojIFByaW50IHRoZSByZXN1bHQNCnByaW50KHRvdF93aXRoaW5zcykNCmBgYA0KQW4gb3ZlcmxhcCBiZXR3ZWVuIGNsdXN0ZXJzIGltcGxpZXMgdGhhdCB0aGVyZSBpcyBhbWJpZ3VpdHkgaW4gdGhlDQphc3NpZ25tZW50IG9mIGRhdGEgcG9pbnRzIHRvIGNsdXN0ZXJzLCBhbmQgdGhlIGNsdXN0ZXJzIG1heSBub3QgYmUNCnN1ZmZpY2llbnRseSBkaXN0aW5jdC4gSW4gc3VjaCBjYXNlcywgaXQgbWlnaHQgYmUgbmVlZGVkIHRvIHJlY29uc2lkZXINCnRoZSBudW1iZXIgb2YgY2x1c3RlcnMgc2luY2UgdGhlIGdvYWwgaXMgdG8gZmluZCBhIGJhbGFuY2UsIGJ1dCB0b28gZmV3DQpjbHVzdGVycyByZXN1bHQgaW4gb3ZlcnNpbXBsaWZpY2F0aW9uLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIG9ic2VydmVkDQpvdmVybGFwLCBhbmQgaXQgaXMgZXZpZGVudCB0aGF0IGZvcm1pbmcgb25seSB0d28gY2x1c3RlcnMgbWF5IG5vdCBiZQ0Kc3VmZmljaWVudCB0byByZXByZXNlbnQgdGhlIGluaGVyZW50IHN0cnVjdHVyZSBvZiB0aGUgZGF0YXNldC4NCg0KDQojQkN1YmVkDQoNCmBgYHtyfQ0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBjKHBhbS5yZXN1bHQkY2x1c3RlcikgDQogDQpzZXQuc2VlZCg3KSANCiANCiMgU3BlY2lmeSB0aGUgbnVtYmVyIG9mIHJvd3MgeW91IHdhbnQgdG8gc2FtcGxlIA0KbnVtX3Jvd3MgPC0gMTAwIA0KIA0KIyBVc2Ugc2FtcGxlIHdpdGggdGhlIHNwZWNpZmllZCBzZWVkIA0KaWR4IDwtIHNhbXBsZSgxOmRpbShIb2JieV9wcm9jKVsxXSwgbnVtX3Jvd3MpIA0KIA0KIyBTZWxlY3QgdGhlIHNhbXBsZWQgcm93cyBmcm9tIEhvYmJ5X3Byb2MgDQpIb2JieV9EYXRhNCA8LSBIb2JieV9wcm9jW2lkeCwgXSANCg0KIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggY2x1c3RlciBhc3NpZ25tZW50cyBhbmQgZ3JvdW5kIHRydXRoIGxhYmVscw0KZGF0YXNldCA8LSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBjbHVzdGVyX2Fzc2lnbm1lbnRzLCBsYWJlbCA9IGdyb3VuZF90cnV0aF9sYWJlbHMpDQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzIDwtIGZ1bmN0aW9uKGRhdGFzZXQpIHsNCiAgbiA8LSBucm93KGRhdGFzZXQpDQogIHByZWNpc2lvbl9zdW0gPC0gMA0KICByZWNhbGxfc3VtIDwtIDANCiANCiAgZm9yIChpIGluIDE6bikgew0KICAgIGNsdXN0ZXIgPC0gZGF0YXNldCRjbHVzdGVyW2ldDQogICAgbGFiZWwgPC0gZGF0YXNldCRsYWJlbFtpXQ0KICAgDQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGZyb20gdGhlIHNhbWUgY2F0ZWdvcnkgaW4gaXRzIGNsdXN0ZXINCiAgICBzYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsW2RhdGFzZXQkY2x1c3RlciA9PSBjbHVzdGVyXSA9PSBsYWJlbCkNCiAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2x1c3Rlcg0KICAgIHNhbWVfY2x1c3RlciA8LSBzdW0oZGF0YXNldCRjbHVzdGVyID09IGNsdXN0ZXIpDQogICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gaXRzIGNhdGVnb3J5DQogICAgdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YXNldCRsYWJlbCA9PSBsYWJlbCkNCiAgIA0KICAgICMgQ2FsY3VsYXRlIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogICAgcHJlY2lzaW9uX3N1bSA8LSBwcmVjaXNpb25fc3VtICsgc2FtZV9jYXRlZ29yeSAvIHNhbWVfY2x1c3Rlcg0KICAgIHJlY2FsbF9zdW0gPC0gcmVjYWxsX3N1bSArIHNhbWVfY2F0ZWdvcnkgLyB0b3RhbF9zYW1lX2NhdGVnb3J5DQogIH0NCiAgIyBFbmQgbG9vcA0KIA0KICAjIENhbGN1bGF0ZSBhdmVyYWdlIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogIHByZWNpc2lvbiA8LSBwcmVjaXNpb25fc3VtIC8gbg0KICByZWNhbGwgPC0gcmVjYWxsX3N1bSAvIG4NCiANCiAgcmV0dXJuKGxpc3QocHJlY2lzaW9uID0gcHJlY2lzaW9uLCByZWNhbGwgPSByZWNhbGwpKQ0KfQ0KDQojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwNCm1ldHJpY3MgPC0gY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzKGRhdGFzZXQpDQpwcmVjaXNpb24gPC0gbWV0cmljcyRwcmVjaXNpb24NCnJlY2FsbCA8LSBtZXRyaWNzJHJlY2FsbA0KDQojIFByaW50IHRoZSByZXN1bHRzDQpjYXQoIkJDdWJlZCBQcmVjaXNpb249ICIsIHByZWNpc2lvbiwgIkFORCBCQ3ViZWQgUmVjYWxsPSAiLCByZWNhbGwsICJcbiIpDQpgYGANCg0KVGhlIHJlbGF0aXZlbHkgaGlnaCByZWNhbGwgY291bGQgYmUgaW5mbHVlbmNlZCBieSB0aGUgc3BlY2lmaWMgY2hvaWNlIG9mDQp0d28gY2x1c3RlcnMgc2luY2UgaXQgaXMgc2Vuc2l0aXZlIHRvIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMuIEluIHRoZQ0KY29udGV4dCBvZiBhIHR3by1jbHVzdGVyIHNvbHV0aW9uLCB0aGUgcmVjYWxsIHNjb3JlIHJlZmxlY3RzIGhvdyB3ZWxsDQp0aGUgYWxnb3JpdGhtIGdyb3VwcyBkYXRhIHBvaW50cyBmcm9tIHRoZSBzYW1lIGNsYXNzIGludG8gb25lIG9mIHRoZSB0d28NCmlkZW50aWZpZWQgY2x1c3RlcnMuIFJlY2FsbCBzdWdnZXN0cyB0aGF0IGEgc2lnbmlmaWNhbnQgcG9ydGlvbiBvZiBkYXRhDQpwb2ludHMgZnJvbSB0aGUgc2FtZSBncm91bmQgdHJ1dGggY2xhc3MgYXJlIGluZGVlZCBncm91cGVkIHRvZ2V0aGVyIGluDQpvbmUgb2YgdGhlIHR3byBjbHVzdGVycy4gSG93ZXZlciwgaXQncyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHRoZSBsb3cNCnByZWNpc2lvbiBzY29yZSAoMC4wNDMyKSBpbmRpY2F0ZXMgYSBsYWNrIG9mIGhvbW9nZW5laXR5IHdpdGhpbiB0aGUNCmlkZW50aWZpZWQgY2x1c3RlcnMsIGltcGx5aW5nIHRoYXQgdGhlIGNsdXN0ZXJzIGNvbnRhaW4gYSBtaXggb2YgZGF0YQ0KcG9pbnRzIGZyb20gZGlmZmVyZW50IGdyb3VuZCB0cnV0aCBjbGFzc2VzLg0KDQoNCg0KDQoNCg0KDQoqKkFTIEEgU3VtbWFyeSBGb3IgY2x1c3RyaW5nKioNClNpbGhvdWV0dGUgYW5hbHlzaXMgbWVhc3VyZXMgaG93IHNpbWlsYXIgYW4gb2JqZWN0IGlzIHRvIGl0cyBvd24gY2x1c3Rlcg0KKGNvaGVzaW9uKSBjb21wYXJlZCB0byBvdGhlciBjbHVzdGVycyAoc2VwYXJhdGlvbikuIFRoZSBzaWxob3VldHRlIHdpZHRoDQpyYW5nZXMgZnJvbSAtMSB0byAxLCB3aGVyZSBhIGhpZ2ggdmFsdWUgaW5kaWNhdGVzIHRoYXQgdGhlIG9iamVjdCBpcw0Kd2VsbCBtYXRjaGVkIHRvIGl0cyBvd24gY2x1c3RlciBhbmQgcG9vcmx5IG1hdGNoZWQgdG8gbmVpZ2hib3JpbmcNCmNsdXN0ZXJzLiBJbiBvdXIgcHJvamVjdCB3ZSBkaWQgY2x1c3RlcnMgZm9yIGs9Mywgaz00LCBhbmQgaz0yIHNpbmNlIGl0cw0KaGF2ZSBoaWdoZXIgYXZlcmFnZSBzaWxob3VldHRlIHRoZW4gb3RoZXIgbnVtYmVyLg0KDQpGb3Igaz0zLCB0aGUgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGlzIDAuMjIuIA0KRm9yIGs9NCwgdGhlIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aCBpcyAwLjE5LiANCkZvciBrPTIsIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggaXMgMC4yMS4NCg0KQSBoaWdoZXIgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGdlbmVyYWxseSBpbmRpY2F0ZXMgYmV0dGVyLWRlZmluZWQNCmNsdXN0ZXJzLiBTbywgaW4gdGhpcyBjYXNlLCBrPTMgaGFzIHRoZSBoaWdoZXN0IGF2ZXJhZ2Ugc2lsaG91ZXR0ZQ0Kd2lkdGguDQoNCkJDdWJlZCBpcyBhIGNsdXN0ZXJpbmcgZXZhbHVhdGlvbiBtZXRyaWMgdGhhdCBjb25zaWRlcnMgYm90aCBwcmVjaXNpb24NCmFuZCByZWNhbGwuIFByZWNpc2lvbiBtZWFzdXJlcyB0aGUgYWNjdXJhY3kgb2YgdGhlIHBvc2l0aXZlIHByZWRpY3Rpb25zLA0Kd2hpbGUgcmVjYWxsIG1lYXN1cmVzIHRoZSBjb3ZlcmFnZSBvZiB0aGUgYWN0dWFsIHBvc2l0aXZlIGluc3RhbmNlcy4NCg0KRm9yIGs9MywgQkJDdWJlZCBQcmVjaXNpb24gPSAwLjA0ODggQkN1YmVkIFJlY2FsbCA9IDAuNDkzMg0KRm9yIGs9NCxCQ3ViZWQgUHJlY2lzaW9uID0gMC4wNTA1IEJDdWJlZCBSZWNhbGwgPSAwLjQxMDENCkZvciBrPTIsIEJDdWJlZFByZWNpc2lvbiA9IDAuMDQzMiBCQ3ViZWQgUmVjYWxsID0gMC42MjUyDQoNClRoZXNlIG1ldHJpY3MgbWVhc3VyZSBob3cgd2VsbCB0aGUgY2x1c3RlcmluZyBhbGlnbnMgd2l0aCB0aGUgZ3JvdW5kDQp0cnV0aC4gSGlnaGVyIHByZWNpc2lvbiBpbmRpY2F0ZXMgZmV3ZXIgZmFsc2UgcG9zaXRpdmVzLCBhbmQgaGlnaGVyDQpyZWNhbGwgaW5kaWNhdGVzIGZld2VyIGZhbHNlIG5lZ2F0aXZlcy4gSGVyZSwgaz0yIGhhcyB0aGUgaGlnaGVzdCByZWNhbGwNCigwLjYyNTIpLCBidXQgaz0zIGhhcyBhIHJlYXNvbmFibGUgYmFsYW5jZSBiZXR3ZWVuIHByZWNpc2lvbiBhbmQgcmVjYWxsLg0KDQpJbiBjb25jbHVzaW9uLCBrPTMgc2VlbXMgdG8gYmUgYSByZWFzb25hYmxlIGNob2ljZS4gSXQgaGFzIGEgZ29vZA0Kc2lsaG91ZXR0ZSB3aWR0aCwgYW5kIGl0cyBCQ3ViZWQgUHJlY2lzaW9uIGFuZCBSZWNhbGwgdmFsdWVzIHN0cmlrZSBhDQpiYWxhbmNlLg0KDQoNCmBgYHtyfQ0KbGlicmFyeShHR2FsbHkpDQpsaWJyYXJ5KHBsb3RseSkNCg0KSG9iYnlfRGF0YTIkY2x1c3RyaW5nIDwtIGFzLmZhY3RvcihIb2JieV9EYXRhMiRjbHVzdGVyKQ0KDQpwPC0gZ2dwYXJjb29yZCAoZGF0YT1Ib2JieV9EYXRhMiwgY29sdW1ucyA9IGMoMToxMSksIGdyb3VwQ29sdW1uID0iY2x1c3RlciIsIHNjYWxlID0gInN0ZCIpKyBsYWJzKHggPSAiIiwgeT0iIiksIHRpdGxlID0gIlBhcmFsbGVsIENvb3JkaW5hdGUgUGxvdCIpDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCg0KDQoNCioqRmluZGluZ3MqKg0KDQoqKlJlZmVyZW5jZXMqKg0KDQoNCioqV2hhdCBpcyB0aGUgYmVzdCBmb3Igb3VyIGRhdGFzZXQ/KioNCg0Kc2luY2UgV2UgdXNlZCB0aGUgSW5mb3JtYXRpb24gR2FpbiwgR2luaSBJbmRleCwgYW5kIEdhaW4gUmF0aW8gYXMgdGhyZWUgaW1wb3J0YW50IG1ldHJpY3MgaW4gb3VyIGNhdGVnb3JpY2FsIGRhdGEgY2xhc3NpZmljYXRpb24gYXBwcm9hY2ggdG8gZGV0ZXJtaW5lIGhvdyBpbXBvcnRhbnQgY2VydGFpbiB2YXJpYWJsZXMgd2VyZSBpbiBwcmVkaWN0aW5nIGhvYmJ5IGNhdGVnb3JpZXMuIFRoZSBkYXRhc2V0IHdhcyBzcGxpdCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMgc28gdGhhdCBkaWZmZXJlbnQgc3Vic2V0cyBvZiB0aGUgZGF0YSBjb3VsZCBiZSB1c2VkIHRvIHRyYWluIGNsYXNzaWZpY2F0aW9uIGFsZ29yaXRobXMuIFRocmVlIG1ldHJpY3Mgd2VyZSB1c2VkIHRvIGV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgbW9kZWw6IG92ZXJhbGwgYWNjdXJhY3ksIHJlY2FsbCwgYW5kIHByZWNpc2lvbi5XZSBleGFtaW5lZCBhY2N1cmFjeSBhdCBzZXZlcmFsIG1lYXN1cmVzIG9mIHRyZWUgc2VsZWN0aW9uLCBQcm92aWRlIHVzIHdpdGggbW9yZSBhY2N1cmF0ZSByZXN1bHRzLCBzcGVjaWZpY2FsbHkgYXQgdGhlIHBydW5pbmcgcGFyYW1ldGVycyBvZiAwLjkwLCAwLjkxLCBhbmQgMC44OSwgaW4gb3JkZXIuVGhlIG1ldGhvZGljYWwgZXZhbHVhdGlvbiB5aWVsZGVkIGluc2lnaHRmdWwgaW5mb3JtYXRpb24gb24gaG93IHdlbGwgZWFjaCBzZWxlY3Rpb24gbWVhc3VyZSBwZXJmb3JtZWQgaW4gcHJvZHVjaW5nIHByZWNpc2UgcHJlZGljdGlvbnMgd2l0aGluIHRoZSBkYXRhc2V0LiBoaWdoIGFjY3VyYWN5IHN0cm9uZ2x5IHN1Z2dlc3QgdGhhdCwgZm9yIG91ciBkYXRhc2V0LCBjbGFzc2lmaWNhdGlvbiBpcyBhIGJldHRlciBtZXRob2RvbG9neSB0aGFuIGNsdXN0ZXJpbmcsIHNpbmNlIHRoZSBjbGFzc2lmaWNhdGlvbiBnaXZlIHVzIGEgZ29vZCByZXN1bHQgdGhlbiBjbHVzdHJpbmcuDQoNCg==
>>>>>>> Stashed changes